mirror of
https://github.com/corda/corda.git
synced 2025-01-15 01:10:33 +00:00
Some long overdue cleaning up of some of the doorman code. (#362)
This commit is contained in:
parent
e9f0c8eca8
commit
2f8836c8ad
@ -1,6 +1,7 @@
|
|||||||
package com.r3.corda.networkmanage.doorman
|
package com.r3.corda.networkmanage.doorman
|
||||||
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||||
|
import com.r3.corda.networkmanage.common.utils.CertPathAndKey
|
||||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||||
import net.corda.cordform.CordformNode
|
import net.corda.cordform.CordformNode
|
||||||
import net.corda.core.crypto.random63BitValue
|
import net.corda.core.crypto.random63BitValue
|
||||||
@ -141,11 +142,11 @@ class NodeRegistrationTest : IntegrationTest() {
|
|||||||
start(
|
start(
|
||||||
serverAddress,
|
serverAddress,
|
||||||
configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)),
|
configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)),
|
||||||
LocalSigner(csrCa.keyPair, arrayOf(csrCa.certificate, rootCaCert)),
|
CertPathAndKey(listOf(csrCa.certificate, rootCaCert), csrCa.keyPair.private),
|
||||||
DoormanConfig(approveAll = true, jiraConfig = null, approveInterval = timeoutMillis),
|
DoormanConfig(approveAll = true, jiraConfig = null, approveInterval = timeoutMillis),
|
||||||
networkParameters?.let {
|
networkParameters?.let {
|
||||||
NetworkMapStartParams(
|
NetworkMapStartParams(
|
||||||
LocalSigner(networkMapCa.keyPair, arrayOf(networkMapCa.certificate, rootCaCert)),
|
LocalSigner(networkMapCa),
|
||||||
networkParameters,
|
networkParameters,
|
||||||
NetworkMapConfig(cacheTimeout = timeoutMillis, signInterval = timeoutMillis)
|
NetworkMapConfig(cacheTimeout = timeoutMillis, signInterval = timeoutMillis)
|
||||||
)
|
)
|
||||||
|
@ -12,6 +12,7 @@ import org.junit.Before
|
|||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class HsmTest {
|
class HsmTest {
|
||||||
@ -19,7 +20,12 @@ class HsmTest {
|
|||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val hsmSimulator: HsmSimulator = HsmSimulator()
|
val hsmSimulator: HsmSimulator = HsmSimulator()
|
||||||
val testParameters = Parameters(
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val tempFolder = TemporaryFolder()
|
||||||
|
|
||||||
|
private val testParameters = Parameters(
|
||||||
dataSourceProperties = mock(),
|
dataSourceProperties = mock(),
|
||||||
device = "${hsmSimulator.port}@${hsmSimulator.host}",
|
device = "${hsmSimulator.port}@${hsmSimulator.host}",
|
||||||
keySpecifier = 1,
|
keySpecifier = 1,
|
||||||
@ -30,10 +36,6 @@ class HsmTest {
|
|||||||
validDays = 3650
|
validDays = 3650
|
||||||
)
|
)
|
||||||
|
|
||||||
@Rule
|
|
||||||
@JvmField
|
|
||||||
val tempFolder = TemporaryFolder()
|
|
||||||
|
|
||||||
private lateinit var inputReader: InputReader
|
private lateinit var inputReader: InputReader
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@ -47,14 +49,12 @@ class HsmTest {
|
|||||||
fun `Authenticator executes the block once user is successfully authenticated`() {
|
fun `Authenticator executes the block once user is successfully authenticated`() {
|
||||||
// given
|
// given
|
||||||
val authenticator = Authenticator(testParameters.createProvider(), inputReader = inputReader)
|
val authenticator = Authenticator(testParameters.createProvider(), inputReader = inputReader)
|
||||||
var executed = false
|
val executed = AtomicBoolean(false)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
authenticator.connectAndAuthenticate({ provider, signers ->
|
authenticator.connectAndAuthenticate { _, _ -> executed.set(true) }
|
||||||
executed = true
|
|
||||||
})
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(executed)
|
assertTrue(executed.get())
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -90,7 +90,12 @@ class SigningServiceIntegrationTest {
|
|||||||
val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true))
|
val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true))
|
||||||
|
|
||||||
NetworkManagementServer().use { server ->
|
NetworkManagementServer().use { server ->
|
||||||
server.start(NetworkHostAndPort(HOST, 0), database, doormanServiceParameter = DoormanConfig(approveAll = true, approveInterval = 2.seconds.toMillis(), jiraConfig = null), startNetworkMap = null)
|
server.start(
|
||||||
|
hostAndPort = NetworkHostAndPort(HOST, 0),
|
||||||
|
database = database,
|
||||||
|
csrCertPathAndKey = null,
|
||||||
|
doormanServiceParameter = DoormanConfig(approveAll = true, approveInterval = 2.seconds.toMillis(), jiraConfig = null),
|
||||||
|
startNetworkMap = null)
|
||||||
val doormanHostAndPort = server.hostAndPort
|
val doormanHostAndPort = server.hostAndPort
|
||||||
// Start Corda network registration.
|
// Start Corda network registration.
|
||||||
val config = createConfig().also {
|
val config = createConfig().also {
|
||||||
|
@ -39,10 +39,9 @@ interface NetworkMapStorage {
|
|||||||
fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters?
|
fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve network map parameters.
|
* Retrieve the network parameters of the current network map, or null if there's no network map.
|
||||||
* @return signed current network map parameters or null if they don't exist
|
|
||||||
*/
|
*/
|
||||||
fun getCurrentSignedNetworkParameters(): SignedNetworkParameters?
|
fun getNetworkParametersOfNetworkMap(): SignedNetworkParameters?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persists given network parameters with signature if provided.
|
* Persists given network parameters with signature if provided.
|
||||||
@ -55,5 +54,5 @@ interface NetworkMapStorage {
|
|||||||
* Note that they may not have been signed up yet.
|
* Note that they may not have been signed up yet.
|
||||||
* @return latest network parameters
|
* @return latest network parameters
|
||||||
*/
|
*/
|
||||||
fun getLatestUnsignedNetworkParameters(): NetworkParameters
|
fun getLatestNetworkParameters(): NetworkParameters?
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,7 @@ import com.r3.corda.networkmanage.common.utils.SignedNetworkParameters
|
|||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.sha256
|
import net.corda.core.crypto.sha256
|
||||||
import net.corda.core.internal.DigitalSignatureWithCert
|
import net.corda.core.internal.DigitalSignatureWithCert
|
||||||
import net.corda.core.serialization.SerializedBytes
|
|
||||||
import net.corda.core.serialization.deserialize
|
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.nodeapi.internal.network.NetworkMap
|
|
||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
|
|
||||||
@ -19,18 +16,16 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence
|
|||||||
class PersistentNetworkMapStorage(private val database: CordaPersistence) : NetworkMapStorage {
|
class PersistentNetworkMapStorage(private val database: CordaPersistence) : NetworkMapStorage {
|
||||||
override fun getCurrentNetworkMap(): SignedNetworkMap? {
|
override fun getCurrentNetworkMap(): SignedNetworkMap? {
|
||||||
return database.transaction {
|
return database.transaction {
|
||||||
getCurrentNetworkMapEntity()?.let {
|
getCurrentNetworkMapEntity()?.toSignedNetworkMap()
|
||||||
val signatureAndCertPath = it.signatureAndCertificate()
|
|
||||||
SignedNetworkMap(SerializedBytes(it.networkMap), signatureAndCertPath)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCurrentSignedNetworkParameters(): SignedNetworkParameters? {
|
override fun getNetworkParametersOfNetworkMap(): SignedNetworkParameters? {
|
||||||
return database.transaction {
|
return database.transaction {
|
||||||
getCurrentNetworkMapEntity()?.let {
|
getCurrentNetworkMapEntity()?.let {
|
||||||
val netParamsHash = it.networkMap.deserialize<NetworkMap>().networkParameterHash
|
val netParamsHash = it.toNetworkMap().networkParameterHash
|
||||||
getSignedNetworkParameters(netParamsHash)
|
getSignedNetworkParameters(netParamsHash) ?:
|
||||||
|
throw IllegalStateException("Current network map is pointing to network parameters that do not exist: $netParamsHash")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,7 +42,9 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters? {
|
override fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters? {
|
||||||
return getNetworkParametersEntity(hash.toString())?.signedParameters()
|
return getNetworkParametersEntity(hash.toString())?.let {
|
||||||
|
if (it.isSigned) it.toSignedNetworkParameters() else null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNodeInfoHashes(certificateStatus: CertificateStatus): List<SecureHash> {
|
override fun getNodeInfoHashes(certificateStatus: CertificateStatus): List<SecureHash> {
|
||||||
@ -79,18 +76,17 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLatestUnsignedNetworkParameters(): NetworkParameters = getLatestNetworkParametersEntity().networkParameters()
|
override fun getLatestNetworkParameters(): NetworkParameters? {
|
||||||
|
|
||||||
private fun getLatestNetworkParametersEntity(): NetworkParametersEntity {
|
|
||||||
return database.transaction {
|
return database.transaction {
|
||||||
val builder = session.criteriaBuilder
|
val query = session.criteriaBuilder.run {
|
||||||
val query = builder.createQuery(NetworkParametersEntity::class.java).run {
|
createQuery(NetworkParametersEntity::class.java).run {
|
||||||
from(NetworkParametersEntity::class.java).run {
|
from(NetworkParametersEntity::class.java).run {
|
||||||
orderBy(builder.desc(get<String>(NetworkParametersEntity::created.name)))
|
orderBy(desc(get<String>(NetworkParametersEntity::created.name)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We just want the last entry
|
// We just want the last entry
|
||||||
session.createQuery(query).setMaxResults(1).resultList.singleOrNull() ?: throw IllegalArgumentException("No network parameters found in network map storage")
|
session.createQuery(query).setMaxResults(1).uniqueResult()?.toNetworkParameters()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
package com.r3.corda.networkmanage.common.persistence.entity
|
package com.r3.corda.networkmanage.common.persistence.entity
|
||||||
|
|
||||||
|
import com.r3.corda.networkmanage.common.utils.SignedNetworkMap
|
||||||
import net.corda.core.internal.DigitalSignatureWithCert
|
import net.corda.core.internal.DigitalSignatureWithCert
|
||||||
|
import net.corda.core.serialization.SerializedBytes
|
||||||
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
|
import net.corda.nodeapi.internal.network.NetworkMap
|
||||||
import javax.persistence.*
|
import javax.persistence.*
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -23,11 +27,12 @@ class NetworkMapEntity(
|
|||||||
@Column(name = "certificate")
|
@Column(name = "certificate")
|
||||||
val certificate: ByteArray
|
val certificate: ByteArray
|
||||||
) {
|
) {
|
||||||
/**
|
fun toNetworkMap(): NetworkMap = networkMap.deserialize()
|
||||||
* Deserializes NetworkMapEntity.signatureBytes into the [DigitalSignatureWithCert] instance
|
|
||||||
*/
|
|
||||||
fun signatureAndCertificate(): DigitalSignatureWithCert {
|
|
||||||
return DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
fun toSignedNetworkMap(): SignedNetworkMap {
|
||||||
|
return SignedNetworkMap(
|
||||||
|
SerializedBytes(networkMap),
|
||||||
|
DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -35,13 +35,15 @@ class NetworkParametersEntity(
|
|||||||
@Column(name = "certificate")
|
@Column(name = "certificate")
|
||||||
val certificate: ByteArray?
|
val certificate: ByteArray?
|
||||||
) {
|
) {
|
||||||
fun networkParameters(): NetworkParameters = parametersBytes.deserialize()
|
val isSigned: Boolean get() = certificate != null && signature != null
|
||||||
|
|
||||||
// Return signed network parameters or null if they haven't been signed yet.
|
fun toNetworkParameters(): NetworkParameters = parametersBytes.deserialize()
|
||||||
fun signedParameters(): SignedNetworkParameters? {
|
|
||||||
return if (certificate != null && signature != null) {
|
fun toSignedNetworkParameters(): SignedNetworkParameters {
|
||||||
val sigWithCert = DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature)
|
if (certificate == null || signature == null) throw IllegalStateException("Network parameters entity is not signed: $parametersHash")
|
||||||
SignedDataWithCert(SerializedBytes(parametersBytes), sigWithCert)
|
return SignedDataWithCert(
|
||||||
} else null
|
SerializedBytes(parametersBytes),
|
||||||
|
DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,20 +4,30 @@ import com.r3.corda.networkmanage.common.persistence.CertificateStatus
|
|||||||
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
||||||
import net.corda.core.internal.SignedDataWithCert
|
import net.corda.core.internal.SignedDataWithCert
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.nodeapi.internal.network.NetworkMap
|
import net.corda.nodeapi.internal.network.NetworkMap
|
||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
|
|
||||||
class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private val signer: Signer) {
|
class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private val signer: Signer) {
|
||||||
|
private companion object {
|
||||||
|
val logger = contextLogger()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs the network map and latest network parameters if they haven't been signed yet.
|
* Signs the network map and latest network parameters if they haven't been signed yet.
|
||||||
*/
|
*/
|
||||||
fun signNetworkMap() {
|
fun signNetworkMap() {
|
||||||
// TODO There is no network parameters update process in place yet. We assume that latest parameters are to be used
|
// TODO There is no network parameters update process in place yet. We assume that latest parameters are to be used
|
||||||
// in current network map.
|
// in current network map.
|
||||||
val latestNetworkParameters = networkMapStorage.getLatestUnsignedNetworkParameters()
|
val latestNetworkParameters = networkMapStorage.getLatestNetworkParameters()
|
||||||
val currentNetworkParameters = networkMapStorage.getCurrentSignedNetworkParameters()
|
if (latestNetworkParameters == null) {
|
||||||
if (currentNetworkParameters?.verified() != latestNetworkParameters)
|
logger.debug("No network parameters present")
|
||||||
signNetworkParameters(latestNetworkParameters)
|
return
|
||||||
|
}
|
||||||
|
val currentNetworkParameters = networkMapStorage.getNetworkParametersOfNetworkMap()
|
||||||
|
if (currentNetworkParameters?.verified() != latestNetworkParameters) {
|
||||||
|
persistSignedNetworkParameters(latestNetworkParameters)
|
||||||
|
}
|
||||||
val currentSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
val currentSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
||||||
val nodeInfoHashes = networkMapStorage.getNodeInfoHashes(CertificateStatus.VALID)
|
val nodeInfoHashes = networkMapStorage.getNodeInfoHashes(CertificateStatus.VALID)
|
||||||
val serialisedNetworkMap = NetworkMap(nodeInfoHashes, latestNetworkParameters.serialize().hash).serialize()
|
val serialisedNetworkMap = NetworkMap(nodeInfoHashes, latestNetworkParameters.serialize().hash).serialize()
|
||||||
@ -27,10 +37,8 @@ class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
fun persistSignedNetworkParameters(networkParameters: NetworkParameters) {
|
||||||
* Signs latest inserted network parameters.
|
logger.info("Signing and persisting network parameters: $networkParameters")
|
||||||
*/
|
|
||||||
fun signNetworkParameters(networkParameters: NetworkParameters) {
|
|
||||||
val digitalSignature = signer.signObject(networkParameters).sig
|
val digitalSignature = signer.signObject(networkParameters).sig
|
||||||
networkMapStorage.saveNetworkParameters(networkParameters, digitalSignature)
|
networkMapStorage.saveNetworkParameters(networkParameters, digitalSignature)
|
||||||
}
|
}
|
||||||
|
@ -10,14 +10,20 @@ import net.corda.core.internal.SignedDataWithCert
|
|||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import net.corda.nodeapi.internal.network.NetworkMap
|
import net.corda.nodeapi.internal.network.NetworkMap
|
||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
|
import java.security.KeyPair
|
||||||
|
import java.security.PrivateKey
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
// TODO These should be defined in node-api
|
// TODO These should be defined in node-api
|
||||||
typealias SignedNetworkParameters = SignedDataWithCert<NetworkParameters>
|
typealias SignedNetworkParameters = SignedDataWithCert<NetworkParameters>
|
||||||
typealias SignedNetworkMap = SignedDataWithCert<NetworkMap>
|
typealias SignedNetworkMap = SignedDataWithCert<NetworkMap>
|
||||||
|
|
||||||
|
data class CertPathAndKey(val certPath: List<X509Certificate>, val key: PrivateKey) {
|
||||||
|
fun toKeyPair(): KeyPair = KeyPair(certPath[0].publicKey, key)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: replace this with Crypto.hash when its available.
|
// TODO: replace this with Crypto.hash when its available.
|
||||||
/**
|
/**
|
||||||
* Returns SHA256 hash of this public key
|
* Returns SHA256 hash of this public key
|
||||||
@ -42,7 +48,7 @@ fun Array<out String>.toConfigWithOptions(registerOptions: OptionParser.() -> Un
|
|||||||
|
|
||||||
class ShowHelpException(val parser: OptionParser, val errorMessage: String? = null) : Exception()
|
class ShowHelpException(val parser: OptionParser, val errorMessage: String? = null) : Exception()
|
||||||
|
|
||||||
fun buildCertPath(vararg certificates: Certificate): CertPath = X509CertificateFactory().delegate.generateCertPath(certificates.asList())
|
fun buildCertPath(vararg certificates: X509Certificate): CertPath = X509CertificateFactory().generateCertPath(certificates.asList())
|
||||||
|
|
||||||
fun buildCertPath(certPathBytes: ByteArray): CertPath = X509CertificateFactory().delegate.generateCertPath(certPathBytes.inputStream())
|
fun buildCertPath(certPathBytes: ByteArray): CertPath = X509CertificateFactory().delegate.generateCertPath(certPathBytes.inputStream())
|
||||||
|
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
package com.r3.corda.networkmanage.doorman
|
package com.r3.corda.networkmanage.doorman
|
||||||
|
|
||||||
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory
|
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage
|
||||||
import com.r3.corda.networkmanage.common.persistence.*
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
||||||
import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
|
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||||
|
import com.r3.corda.networkmanage.common.utils.CertPathAndKey
|
||||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||||
import com.r3.corda.networkmanage.doorman.signer.DefaultCsrHandler
|
|
||||||
import com.r3.corda.networkmanage.doorman.signer.JiraCsrHandler
|
|
||||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||||
import com.r3.corda.networkmanage.doorman.webservice.MonitoringWebService
|
|
||||||
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService
|
|
||||||
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService
|
|
||||||
import com.r3.corda.networkmanage.hsm.configuration.Parameters.Companion.DEFAULT_CSR_CERTIFICATE_NAME
|
import com.r3.corda.networkmanage.hsm.configuration.Parameters.Companion.DEFAULT_CSR_CERTIFICATE_NAME
|
||||||
import com.r3.corda.networkmanage.hsm.configuration.Parameters.Companion.DEFAULT_NETWORK_MAP_CERTIFICATE_NAME
|
import com.r3.corda.networkmanage.hsm.configuration.Parameters.Companion.DEFAULT_NETWORK_MAP_CERTIFICATE_NAME
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
@ -21,141 +16,19 @@ import net.corda.core.internal.div
|
|||||||
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
import net.corda.core.serialization.internal.SerializationEnvironmentImpl
|
||||||
import net.corda.core.serialization.internal.nodeSerializationEnv
|
import net.corda.core.serialization.internal.nodeSerializationEnv
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.loggerFor
|
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
|
||||||
import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT
|
import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT
|
||||||
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
|
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme
|
import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
import java.io.Closeable
|
|
||||||
import java.net.URI
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import javax.security.auth.x500.X500Principal
|
import javax.security.auth.x500.X500Principal
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
class NetworkManagementServer : Closeable {
|
|
||||||
private val doOnClose = mutableListOf<() -> Unit>()
|
|
||||||
lateinit var hostAndPort: NetworkHostAndPort
|
|
||||||
|
|
||||||
override fun close() = doOnClose.forEach { it() }
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val logger = loggerFor<NetworkManagementServer>()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getNetworkMapService(config: NetworkMapConfig, database: CordaPersistence, signer: LocalSigner?, updateNetworkParameters: NetworkParameters?): NodeInfoWebService {
|
|
||||||
val networkMapStorage = PersistentNetworkMapStorage(database)
|
|
||||||
val nodeInfoStorage = PersistentNodeInfoStorage(database)
|
|
||||||
val localNetworkMapSigner = if (signer != null) NetworkMapSigner(networkMapStorage, signer) else null
|
|
||||||
|
|
||||||
updateNetworkParameters?.let {
|
|
||||||
// Persisting new network parameters
|
|
||||||
val currentNetworkParameters = networkMapStorage.getCurrentSignedNetworkParameters()
|
|
||||||
if (currentNetworkParameters == null) {
|
|
||||||
localNetworkMapSigner?.signNetworkParameters(it) ?: networkMapStorage.saveNetworkParameters(it, null)
|
|
||||||
} else {
|
|
||||||
throw UnsupportedOperationException("Network parameters already exist. Updating them via the file config is not supported yet.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This call will fail if parameter is null in DB.
|
|
||||||
try {
|
|
||||||
val latestParameter = networkMapStorage.getLatestUnsignedNetworkParameters()
|
|
||||||
logger.info("Starting network map service with network parameters : $latestParameter")
|
|
||||||
} catch (e: NoSuchElementException) {
|
|
||||||
logger.error("No network parameter found, please upload new network parameter before starting network map service. The server will now exit.")
|
|
||||||
exitProcess(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thread sign network map in case of change (i.e. a new node info has been added or a node info has been removed).
|
|
||||||
if (localNetworkMapSigner != null) {
|
|
||||||
val scheduledExecutor = Executors.newScheduledThreadPool(1)
|
|
||||||
val signingThread = Runnable {
|
|
||||||
try {
|
|
||||||
localNetworkMapSigner.signNetworkMap()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
// Log the error and carry on.
|
|
||||||
logger.error("Error encountered when processing node info changes.", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scheduledExecutor.scheduleAtFixedRate(signingThread, config.signInterval, config.signInterval, TimeUnit.MILLISECONDS)
|
|
||||||
doOnClose += { scheduledExecutor.shutdown() }
|
|
||||||
}
|
|
||||||
|
|
||||||
return NodeInfoWebService(nodeInfoStorage, networkMapStorage, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun getDoormanService(config: DoormanConfig, database: CordaPersistence, signer: LocalSigner?, serverStatus: NetworkManagementServerStatus): RegistrationWebService {
|
|
||||||
logger.info("Starting Doorman server.")
|
|
||||||
val requestService = if (config.approveAll) {
|
|
||||||
logger.warn("Doorman server is in 'Approve All' mode, this will approve all incoming certificate signing requests.")
|
|
||||||
ApproveAllCertificateRequestStorage(PersistentCertificateRequestStorage(database))
|
|
||||||
} else {
|
|
||||||
PersistentCertificateRequestStorage(database)
|
|
||||||
}
|
|
||||||
|
|
||||||
val jiraConfig = config.jiraConfig
|
|
||||||
val requestProcessor = if (jiraConfig != null) {
|
|
||||||
val jiraWebAPI = AsynchronousJiraRestClientFactory().createWithBasicHttpAuthentication(URI(jiraConfig.address), jiraConfig.username, jiraConfig.password)
|
|
||||||
val jiraClient = JiraClient(jiraWebAPI, jiraConfig.projectCode, jiraConfig.doneTransitionCode)
|
|
||||||
JiraCsrHandler(jiraClient, requestService, DefaultCsrHandler(requestService, signer))
|
|
||||||
} else {
|
|
||||||
DefaultCsrHandler(requestService, signer)
|
|
||||||
}
|
|
||||||
|
|
||||||
val scheduledExecutor = Executors.newScheduledThreadPool(1)
|
|
||||||
val approvalThread = Runnable {
|
|
||||||
try {
|
|
||||||
serverStatus.lastRequestCheckTime = Instant.now()
|
|
||||||
// Create tickets for requests which don't have one yet.
|
|
||||||
requestProcessor.createTickets()
|
|
||||||
// Process Jira approved tickets.
|
|
||||||
requestProcessor.processApprovedRequests()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
// Log the error and carry on.
|
|
||||||
logger.error("Error encountered when approving request.", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scheduledExecutor.scheduleAtFixedRate(approvalThread, config.approveInterval, config.approveInterval, TimeUnit.MILLISECONDS)
|
|
||||||
doOnClose += { scheduledExecutor.shutdown() }
|
|
||||||
|
|
||||||
return RegistrationWebService(requestProcessor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun start(hostAndPort: NetworkHostAndPort,
|
|
||||||
database: CordaPersistence,
|
|
||||||
doormanSigner: LocalSigner? = null,
|
|
||||||
doormanServiceParameter: DoormanConfig?, // TODO Doorman config shouldn't be optional as the doorman is always required to run
|
|
||||||
startNetworkMap: NetworkMapStartParams?
|
|
||||||
) {
|
|
||||||
val services = mutableListOf<Any>()
|
|
||||||
val serverStatus = NetworkManagementServerStatus()
|
|
||||||
|
|
||||||
startNetworkMap?.let { services += getNetworkMapService(it.config, database, it.signer, it.updateNetworkParameters) }
|
|
||||||
doormanServiceParameter?.let { services += getDoormanService(it, database, doormanSigner, serverStatus) }
|
|
||||||
|
|
||||||
require(services.isNotEmpty()) { "No service created, please provide at least one service config." }
|
|
||||||
|
|
||||||
// TODO: use mbean to expose audit data?
|
|
||||||
services += MonitoringWebService(serverStatus)
|
|
||||||
|
|
||||||
val webServer = NetworkManagementWebServer(hostAndPort, *services.toTypedArray())
|
|
||||||
webServer.start()
|
|
||||||
|
|
||||||
doOnClose += webServer::close
|
|
||||||
this.hostAndPort = webServer.hostAndPort
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class NetworkMapStartParams(val signer: LocalSigner?, val updateNetworkParameters: NetworkParameters?, val config: NetworkMapConfig)
|
data class NetworkMapStartParams(val signer: LocalSigner?, val updateNetworkParameters: NetworkParameters?, val config: NetworkMapConfig)
|
||||||
|
|
||||||
data class NetworkManagementServerStatus(var serverStartTime: Instant = Instant.now(), var lastRequestCheckTime: Instant? = null)
|
data class NetworkManagementServerStatus(var serverStartTime: Instant = Instant.now(), var lastRequestCheckTime: Instant? = null)
|
||||||
@ -181,9 +54,8 @@ fun generateRootKeyPair(rootStoreFile: Path, rootKeystorePass: String?, rootPriv
|
|||||||
val rootPrivateKeyPassword = rootPrivateKeyPass ?: readPassword("Root Private Key Password: ")
|
val rootPrivateKeyPassword = rootPrivateKeyPass ?: readPassword("Root Private Key Password: ")
|
||||||
|
|
||||||
if (rootStore.containsAlias(X509Utilities.CORDA_ROOT_CA)) {
|
if (rootStore.containsAlias(X509Utilities.CORDA_ROOT_CA)) {
|
||||||
val oldKey = loadOrCreateKeyStore(rootStoreFile, rootKeystorePassword).getCertificate(X509Utilities.CORDA_ROOT_CA).publicKey
|
println("${X509Utilities.CORDA_ROOT_CA} already exists in keystore, process will now terminate.")
|
||||||
println("Key ${X509Utilities.CORDA_ROOT_CA} already exists in keystore, process will now terminate.")
|
println(rootStore.getCertificate(X509Utilities.CORDA_ROOT_CA))
|
||||||
println(oldKey)
|
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +75,7 @@ fun generateRootKeyPair(rootStoreFile: Path, rootKeystorePass: String?, rootPriv
|
|||||||
println("Trust store for distribution to nodes created in $nodeTrustStore")
|
println("Trust store for distribution to nodes created in $nodeTrustStore")
|
||||||
|
|
||||||
println("Root CA keypair and certificate stored in ${rootStoreFile.toAbsolutePath()}.")
|
println("Root CA keypair and certificate stored in ${rootStoreFile.toAbsolutePath()}.")
|
||||||
println(loadKeyStore(rootStoreFile, rootKeystorePassword).getCertificate(X509Utilities.CORDA_ROOT_CA).publicKey)
|
println(selfSignCert)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateSigningKeyPairs(keystoreFile: Path, rootStoreFile: Path, rootKeystorePass: String?, rootPrivateKeyPass: String?, keystorePass: String?, caPrivateKeyPass: String?) {
|
fun generateSigningKeyPairs(keystoreFile: Path, rootStoreFile: Path, rootKeystorePass: String?, rootPrivateKeyPass: String?, keystorePass: String?, caPrivateKeyPass: String?) {
|
||||||
@ -262,7 +134,7 @@ fun generateSigningKeyPairs(keystoreFile: Path, rootStoreFile: Path, rootKeystor
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun buildLocalSigners(parameters: NetworkManagementServerParameters): Pair<LocalSigner, LocalSigner>? {
|
private fun processKeyStore(parameters: NetworkManagementServerParameters): Pair<CertPathAndKey, LocalSigner>? {
|
||||||
if (parameters.keystorePath == null) return null
|
if (parameters.keystorePath == null) return null
|
||||||
|
|
||||||
// Get password from console if not in config.
|
// Get password from console if not in config.
|
||||||
@ -270,19 +142,22 @@ private fun buildLocalSigners(parameters: NetworkManagementServerParameters): Pa
|
|||||||
val privateKeyPassword = parameters.caPrivateKeyPassword ?: readPassword("Private key password: ")
|
val privateKeyPassword = parameters.caPrivateKeyPassword ?: readPassword("Private key password: ")
|
||||||
val keyStore = loadOrCreateKeyStore(parameters.keystorePath, keyStorePassword)
|
val keyStore = loadOrCreateKeyStore(parameters.keystorePath, keyStorePassword)
|
||||||
|
|
||||||
val (doormanSigner, networkMapSigner) = listOf(DEFAULT_CSR_CERTIFICATE_NAME, DEFAULT_NETWORK_MAP_CERTIFICATE_NAME).map {
|
val csrCertPathAndKey = keyStore.run {
|
||||||
val keyPair = keyStore.getKeyPair(it, privateKeyPassword)
|
CertPathAndKey(
|
||||||
val certPath = keyStore.getCertificateChain(it).map { it as X509Certificate }
|
keyStore.getCertificateChain(DEFAULT_CSR_CERTIFICATE_NAME).map { it as X509Certificate },
|
||||||
LocalSigner(keyPair, certPath.toTypedArray())
|
keyStore.getSupportedKey(DEFAULT_CSR_CERTIFICATE_NAME, privateKeyPassword)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Pair(doormanSigner, networkMapSigner)
|
val networkMapSigner = LocalSigner(keyStore.getCertificateAndKeyPair(DEFAULT_NETWORK_MAP_CERTIFICATE_NAME, privateKeyPassword))
|
||||||
|
|
||||||
|
return Pair(csrCertPathAndKey, networkMapSigner)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This storage automatically approves all created requests.
|
* This storage automatically approves all created requests.
|
||||||
*/
|
*/
|
||||||
private class ApproveAllCertificateRequestStorage(private val delegate: CertificationRequestStorage) : CertificationRequestStorage by delegate {
|
class ApproveAllCertificateRequestStorage(private val delegate: CertificationRequestStorage) : CertificationRequestStorage by delegate {
|
||||||
override fun saveRequest(request: PKCS10CertificationRequest): String {
|
override fun saveRequest(request: PKCS10CertificationRequest): String {
|
||||||
val requestId = delegate.saveRequest(request)
|
val requestId = delegate.saveRequest(request)
|
||||||
delegate.markRequestTicketCreated(requestId)
|
delegate.markRequestTicketCreated(requestId)
|
||||||
@ -311,9 +186,9 @@ fun main(args: Array<String>) {
|
|||||||
initialiseSerialization()
|
initialiseSerialization()
|
||||||
val database = configureDatabase(dataSourceProperties)
|
val database = configureDatabase(dataSourceProperties)
|
||||||
// TODO: move signing to signing server.
|
// TODO: move signing to signing server.
|
||||||
val localSigners = buildLocalSigners(this)
|
val csrAndNetworkMap = processKeyStore(this)
|
||||||
|
|
||||||
if (localSigners != null) {
|
if (csrAndNetworkMap != null) {
|
||||||
println("Starting network management services with local signing")
|
println("Starting network management services with local signing")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,10 +200,10 @@ fun main(args: Array<String>) {
|
|||||||
parseNetworkParametersFrom(it)
|
parseNetworkParametersFrom(it)
|
||||||
}
|
}
|
||||||
val networkMapStartParams = networkMapConfig?.let {
|
val networkMapStartParams = networkMapConfig?.let {
|
||||||
NetworkMapStartParams(localSigners?.second, networkParameters, it)
|
NetworkMapStartParams(csrAndNetworkMap?.second, networkParameters, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
networkManagementServer.start(NetworkHostAndPort(host, port), database, localSigners?.first, doormanConfig, networkMapStartParams)
|
networkManagementServer.start(NetworkHostAndPort(host, port), database, csrAndNetworkMap?.first, doormanConfig, networkMapStartParams)
|
||||||
|
|
||||||
Runtime.getRuntime().addShutdownHook(thread(start = false) {
|
Runtime.getRuntime().addShutdownHook(thread(start = false) {
|
||||||
networkManagementServer.close()
|
networkManagementServer.close()
|
||||||
|
@ -1,58 +1,142 @@
|
|||||||
package com.r3.corda.networkmanage.doorman
|
package com.r3.corda.networkmanage.doorman
|
||||||
|
|
||||||
|
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory
|
||||||
|
import com.r3.corda.networkmanage.common.persistence.PersistentCertificateRequestStorage
|
||||||
|
import com.r3.corda.networkmanage.common.persistence.PersistentNetworkMapStorage
|
||||||
|
import com.r3.corda.networkmanage.common.persistence.PersistentNodeInfoStorage
|
||||||
|
import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
|
||||||
|
import com.r3.corda.networkmanage.common.utils.CertPathAndKey
|
||||||
|
import com.r3.corda.networkmanage.doorman.signer.DefaultCsrHandler
|
||||||
|
import com.r3.corda.networkmanage.doorman.signer.JiraCsrHandler
|
||||||
|
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||||
|
import com.r3.corda.networkmanage.doorman.webservice.MonitoringWebService
|
||||||
|
import com.r3.corda.networkmanage.doorman.webservice.NetworkMapWebService
|
||||||
|
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import org.eclipse.jetty.server.Server
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import org.eclipse.jetty.server.ServerConnector
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import org.eclipse.jetty.server.handler.HandlerCollection
|
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler
|
|
||||||
import org.eclipse.jetty.servlet.ServletHolder
|
|
||||||
import org.glassfish.jersey.server.ResourceConfig
|
|
||||||
import org.glassfish.jersey.servlet.ServletContainer
|
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.net.InetSocketAddress
|
import java.net.URI
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
/**
|
class NetworkManagementServer : Closeable {
|
||||||
* NetworkManagementWebServer runs on Jetty server and provides service via http.
|
|
||||||
*/
|
|
||||||
class NetworkManagementWebServer(hostAndPort: NetworkHostAndPort, private vararg val webServices: Any) : Closeable {
|
|
||||||
companion object {
|
companion object {
|
||||||
val logger = loggerFor<NetworkManagementServer>()
|
private val logger = loggerFor<NetworkManagementServer>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val server: Server = Server(InetSocketAddress(hostAndPort.host, hostAndPort.port)).apply {
|
private val closeActions = mutableListOf<() -> Unit>()
|
||||||
handler = HandlerCollection().apply {
|
lateinit var hostAndPort: NetworkHostAndPort
|
||||||
addHandler(buildServletContextHandler())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val hostAndPort: NetworkHostAndPort
|
|
||||||
get() = server.connectors.mapNotNull { it as? ServerConnector }
|
|
||||||
.map { NetworkHostAndPort(it.host, it.localPort) }
|
|
||||||
.first()
|
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
logger.info("Shutting down network management web services...")
|
for (closeAction in closeActions) {
|
||||||
server.stop()
|
try {
|
||||||
server.join()
|
closeAction()
|
||||||
}
|
} catch (e: Exception) {
|
||||||
|
logger.warn("Discregarding exception thrown during close", e)
|
||||||
fun start() {
|
|
||||||
logger.info("Starting network management web services...")
|
|
||||||
server.start()
|
|
||||||
logger.info("Network management web services started on $hostAndPort with ${webServices.map { it.javaClass.simpleName }}")
|
|
||||||
println("Network management web services started on $hostAndPort with ${webServices.map { it.javaClass.simpleName }}")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildServletContextHandler(): ServletContextHandler {
|
|
||||||
return ServletContextHandler().apply {
|
|
||||||
contextPath = "/"
|
|
||||||
val resourceConfig = ResourceConfig().apply {
|
|
||||||
// Add your API provider classes (annotated for JAX-RS) here
|
|
||||||
webServices.forEach { register(it) }
|
|
||||||
}
|
}
|
||||||
val jerseyServlet = ServletHolder(ServletContainer(resourceConfig)).apply { initOrder = 0 }// Initialise at server start
|
|
||||||
addServlet(jerseyServlet, "/*")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getNetworkMapService(config: NetworkMapConfig, database: CordaPersistence, signer: LocalSigner?, newNetworkParameters: NetworkParameters?): NetworkMapWebService {
|
||||||
|
val networkMapStorage = PersistentNetworkMapStorage(database)
|
||||||
|
val nodeInfoStorage = PersistentNodeInfoStorage(database)
|
||||||
|
val localNetworkMapSigner = signer?.let { NetworkMapSigner(networkMapStorage, it) }
|
||||||
|
|
||||||
|
newNetworkParameters?.let {
|
||||||
|
val netParamsOfNetworkMap = networkMapStorage.getNetworkParametersOfNetworkMap()
|
||||||
|
if (netParamsOfNetworkMap == null) {
|
||||||
|
localNetworkMapSigner?.persistSignedNetworkParameters(it) ?: networkMapStorage.saveNetworkParameters(it, null)
|
||||||
|
} else {
|
||||||
|
throw UnsupportedOperationException("Network parameters already exist. Updating them is not supported yet.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val latestParameters = networkMapStorage.getLatestNetworkParameters() ?:
|
||||||
|
throw IllegalStateException("No network parameters were found. Please upload new network parameters before starting network map service")
|
||||||
|
logger.info("Starting network map service with network parameters: $latestParameters")
|
||||||
|
|
||||||
|
if (localNetworkMapSigner != null) {
|
||||||
|
logger.info("Starting background worker for signing the network map using the local key store")
|
||||||
|
val scheduledExecutor = Executors.newScheduledThreadPool(1)
|
||||||
|
scheduledExecutor.scheduleAtFixedRate({
|
||||||
|
try {
|
||||||
|
localNetworkMapSigner.signNetworkMap()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Log the error and carry on.
|
||||||
|
logger.error("Unable to sign network map", e)
|
||||||
|
}
|
||||||
|
}, config.signInterval, config.signInterval, TimeUnit.MILLISECONDS)
|
||||||
|
closeActions += scheduledExecutor::shutdown
|
||||||
|
}
|
||||||
|
|
||||||
|
return NetworkMapWebService(nodeInfoStorage, networkMapStorage, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getDoormanService(config: DoormanConfig,
|
||||||
|
database: CordaPersistence,
|
||||||
|
csrCertPathAndKey: CertPathAndKey?,
|
||||||
|
serverStatus: NetworkManagementServerStatus): RegistrationWebService {
|
||||||
|
logger.info("Starting Doorman server.")
|
||||||
|
val requestService = if (config.approveAll) {
|
||||||
|
logger.warn("Doorman server is in 'Approve All' mode, this will approve all incoming certificate signing requests.")
|
||||||
|
ApproveAllCertificateRequestStorage(PersistentCertificateRequestStorage(database))
|
||||||
|
} else {
|
||||||
|
PersistentCertificateRequestStorage(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
val jiraConfig = config.jiraConfig
|
||||||
|
val requestProcessor = if (jiraConfig != null) {
|
||||||
|
val jiraWebAPI = AsynchronousJiraRestClientFactory().createWithBasicHttpAuthentication(URI(jiraConfig.address), jiraConfig.username, jiraConfig.password)
|
||||||
|
val jiraClient = JiraClient(jiraWebAPI, jiraConfig.projectCode, jiraConfig.doneTransitionCode)
|
||||||
|
JiraCsrHandler(jiraClient, requestService, DefaultCsrHandler(requestService, csrCertPathAndKey))
|
||||||
|
} else {
|
||||||
|
DefaultCsrHandler(requestService, csrCertPathAndKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
val scheduledExecutor = Executors.newScheduledThreadPool(1)
|
||||||
|
val approvalThread = Runnable {
|
||||||
|
try {
|
||||||
|
serverStatus.lastRequestCheckTime = Instant.now()
|
||||||
|
// Create tickets for requests which don't have one yet.
|
||||||
|
requestProcessor.createTickets()
|
||||||
|
// Process Jira approved tickets.
|
||||||
|
requestProcessor.processApprovedRequests()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Log the error and carry on.
|
||||||
|
logger.error("Error encountered when approving request.", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scheduledExecutor.scheduleAtFixedRate(approvalThread, config.approveInterval, config.approveInterval, TimeUnit.MILLISECONDS)
|
||||||
|
closeActions += scheduledExecutor::shutdown
|
||||||
|
|
||||||
|
return RegistrationWebService(requestProcessor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun start(hostAndPort: NetworkHostAndPort,
|
||||||
|
database: CordaPersistence,
|
||||||
|
csrCertPathAndKey: CertPathAndKey?,
|
||||||
|
doormanServiceParameter: DoormanConfig?, // TODO Doorman config shouldn't be optional as the doorman is always required to run
|
||||||
|
startNetworkMap: NetworkMapStartParams?
|
||||||
|
) {
|
||||||
|
val services = mutableListOf<Any>()
|
||||||
|
val serverStatus = NetworkManagementServerStatus()
|
||||||
|
|
||||||
|
startNetworkMap?.let { services += getNetworkMapService(it.config, database, it.signer, it.updateNetworkParameters) }
|
||||||
|
doormanServiceParameter?.let { services += getDoormanService(it, database, csrCertPathAndKey, serverStatus) }
|
||||||
|
|
||||||
|
require(services.isNotEmpty()) { "No service created, please provide at least one service config." }
|
||||||
|
|
||||||
|
// TODO: use mbean to expose audit data?
|
||||||
|
services += MonitoringWebService(serverStatus)
|
||||||
|
|
||||||
|
val webServer = NetworkManagementWebServer(hostAndPort, *services.toTypedArray())
|
||||||
|
webServer.start()
|
||||||
|
|
||||||
|
closeActions += webServer::close
|
||||||
|
this.hostAndPort = webServer.hostAndPort
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
package com.r3.corda.networkmanage.doorman
|
||||||
|
|
||||||
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
|
import net.corda.core.utilities.contextLogger
|
||||||
|
import net.corda.core.utilities.loggerFor
|
||||||
|
import org.eclipse.jetty.server.Server
|
||||||
|
import org.eclipse.jetty.server.ServerConnector
|
||||||
|
import org.eclipse.jetty.server.handler.HandlerCollection
|
||||||
|
import org.eclipse.jetty.servlet.ServletContextHandler
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder
|
||||||
|
import org.glassfish.jersey.server.ResourceConfig
|
||||||
|
import org.glassfish.jersey.servlet.ServletContainer
|
||||||
|
import java.io.Closeable
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NetworkManagementWebServer runs on Jetty server and provides service via http.
|
||||||
|
*/
|
||||||
|
class NetworkManagementWebServer(hostAndPort: NetworkHostAndPort, private vararg val webServices: Any) : Closeable {
|
||||||
|
companion object {
|
||||||
|
val logger = contextLogger()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val server: Server = Server(InetSocketAddress(hostAndPort.host, hostAndPort.port)).apply {
|
||||||
|
handler = HandlerCollection().apply {
|
||||||
|
addHandler(buildServletContextHandler())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val hostAndPort: NetworkHostAndPort
|
||||||
|
get() = server.connectors.mapNotNull { it as? ServerConnector }
|
||||||
|
.map { NetworkHostAndPort(it.host, it.localPort) }
|
||||||
|
.first()
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
logger.info("Shutting down network management web services...")
|
||||||
|
server.stop()
|
||||||
|
server.join()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun start() {
|
||||||
|
logger.info("Starting network management web services...")
|
||||||
|
server.start()
|
||||||
|
logger.info("Network management web services started on $hostAndPort with ${webServices.map { it.javaClass.simpleName }}")
|
||||||
|
println("Network management web services started on $hostAndPort with ${webServices.map { it.javaClass.simpleName }}")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildServletContextHandler(): ServletContextHandler {
|
||||||
|
return ServletContextHandler().apply {
|
||||||
|
contextPath = "/"
|
||||||
|
val resourceConfig = ResourceConfig().apply {
|
||||||
|
// Add your API provider classes (annotated for JAX-RS) here
|
||||||
|
webServices.forEach { register(it) }
|
||||||
|
}
|
||||||
|
val jerseyServlet = ServletHolder(ServletContainer(resourceConfig)).apply { initOrder = 0 }// Initialise at server start
|
||||||
|
addServlet(jerseyServlet, "/*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,20 @@
|
|||||||
package com.r3.corda.networkmanage.doorman.signer
|
package com.r3.corda.networkmanage.doorman.signer
|
||||||
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
||||||
import com.r3.corda.networkmanage.common.persistence.CertificateSigningRequest
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage
|
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage
|
||||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
||||||
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
||||||
import com.r3.corda.networkmanage.doorman.JiraClient
|
import com.r3.corda.networkmanage.common.utils.CertPathAndKey
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
|
import org.bouncycastle.asn1.x509.GeneralName
|
||||||
|
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||||
|
import org.bouncycastle.asn1.x509.NameConstraints
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
|
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||||
|
import java.security.cert.CertPath
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
interface CsrHandler {
|
interface CsrHandler {
|
||||||
fun saveRequest(rawRequest: PKCS10CertificationRequest): String
|
fun saveRequest(rawRequest: PKCS10CertificationRequest): String
|
||||||
@ -16,26 +23,21 @@ interface CsrHandler {
|
|||||||
fun getResponse(requestId: String): CertificateResponse
|
fun getResponse(requestId: String): CertificateResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
class DefaultCsrHandler(private val storage: CertificationRequestStorage, private val signer: LocalSigner?) : CsrHandler {
|
class DefaultCsrHandler(private val storage: CertificationRequestStorage,
|
||||||
|
private val csrCertPathAndKey: CertPathAndKey?) : CsrHandler {
|
||||||
|
|
||||||
override fun processApprovedRequests() {
|
override fun processApprovedRequests() {
|
||||||
storage.getRequests(RequestStatus.APPROVED)
|
if (csrCertPathAndKey == null) return
|
||||||
.forEach { processRequest(it.requestId, it.request) }
|
storage.getRequests(RequestStatus.APPROVED).forEach {
|
||||||
|
val nodeCertPath = createSignedNodeCertificate(it.request, csrCertPathAndKey)
|
||||||
|
// Since Doorman is deployed in the auto-signing mode, we use DOORMAN_SIGNATURE as the signer.
|
||||||
|
storage.putCertificatePath(it.requestId, nodeCertPath, listOf(DOORMAN_SIGNATURE))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createTickets() {}
|
override fun createTickets() {}
|
||||||
|
|
||||||
private fun processRequest(requestId: String, request: PKCS10CertificationRequest) {
|
override fun saveRequest(rawRequest: PKCS10CertificationRequest): String = storage.saveRequest(rawRequest)
|
||||||
if (signer != null) {
|
|
||||||
val certs = signer.createSignedClientCertificate(request)
|
|
||||||
// Since Doorman is deployed in the auto-signing mode (i.e. signer != null),
|
|
||||||
// we use DOORMAN_SIGNATURE as the signer.
|
|
||||||
storage.putCertificatePath(requestId, certs, listOf(DOORMAN_SIGNATURE))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun saveRequest(rawRequest: PKCS10CertificationRequest): String {
|
|
||||||
return storage.saveRequest(rawRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getResponse(requestId: String): CertificateResponse {
|
override fun getResponse(requestId: String): CertificateResponse {
|
||||||
val response = storage.getRequest(requestId)
|
val response = storage.getRequest(requestId)
|
||||||
@ -45,63 +47,25 @@ class DefaultCsrHandler(private val storage: CertificationRequestStorage, privat
|
|||||||
RequestStatus.SIGNED -> CertificateResponse.Ready(response.certData?.certPath ?: throw IllegalArgumentException("Certificate should not be null."))
|
RequestStatus.SIGNED -> CertificateResponse.Ready(response.certData?.certPath ?: throw IllegalArgumentException("Certificate should not be null."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class JiraCsrHandler(private val jiraClient: JiraClient, private val storage: CertificationRequestStorage, private val delegate: CsrHandler) : CsrHandler by delegate {
|
private fun createSignedNodeCertificate(certificationRequest: PKCS10CertificationRequest,
|
||||||
private companion object {
|
csrCertPathAndKey: CertPathAndKey): CertPath {
|
||||||
val log = loggerFor<JiraCsrHandler>()
|
// The sub certs issued by the client must satisfy this directory name (or legal name in Corda) constraints,
|
||||||
}
|
// sub certs' directory name must be within client CA's name's subtree,
|
||||||
|
// please see [sun.security.x509.X500Name.isWithinSubtree()] for more information.
|
||||||
override fun saveRequest(rawRequest: PKCS10CertificationRequest): String {
|
// We assume all attributes in the subject name has been checked prior approval.
|
||||||
val requestId = delegate.saveRequest(rawRequest)
|
// TODO: add validation to subject name.
|
||||||
// Make sure request has been accepted.
|
val request = JcaPKCS10CertificationRequest(certificationRequest)
|
||||||
try {
|
val nameConstraints = NameConstraints(
|
||||||
if (delegate.getResponse(requestId) !is CertificateResponse.Unauthorised) {
|
arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, request.subject))),
|
||||||
jiraClient.createRequestTicket(requestId, rawRequest)
|
arrayOf())
|
||||||
storage.markRequestTicketCreated(requestId)
|
val nodeCaCert = X509Utilities.createCertificate(
|
||||||
}
|
CertificateType.NODE_CA,
|
||||||
} catch (e: Exception) {
|
csrCertPathAndKey.certPath[0],
|
||||||
log.warn("There was an error while creating Jira tickets", e)
|
csrCertPathAndKey.toKeyPair(),
|
||||||
} finally {
|
X500Principal(request.subject.encoded),
|
||||||
return requestId
|
request.publicKey,
|
||||||
}
|
nameConstraints = nameConstraints)
|
||||||
}
|
return X509CertificateFactory().generateCertPath(listOf(nodeCaCert) + csrCertPathAndKey.certPath)
|
||||||
|
|
||||||
override fun processApprovedRequests() {
|
|
||||||
val approvedRequest = jiraClient.getApprovedRequests()
|
|
||||||
approvedRequest.forEach { (id, approvedBy) -> storage.approveRequest(id, approvedBy) }
|
|
||||||
delegate.processApprovedRequests()
|
|
||||||
|
|
||||||
val signedRequests = approvedRequest.mapNotNull { (id, _) ->
|
|
||||||
val request = storage.getRequest(id)
|
|
||||||
|
|
||||||
if (request != null && request.status == RequestStatus.SIGNED) {
|
|
||||||
request.certData?.certPath?.let { certs -> id to certs }
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}.toMap()
|
|
||||||
jiraClient.updateSignedRequests(signedRequests)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates Jira tickets for all request in [RequestStatus.NEW] state.
|
|
||||||
*
|
|
||||||
* Usually requests are expected to move to the [RequestStatus.TICKET_CREATED] state immediately,
|
|
||||||
* they might be left in the [RequestStatus.NEW] state if Jira is down.
|
|
||||||
*/
|
|
||||||
override fun createTickets() {
|
|
||||||
try {
|
|
||||||
for (signingRequest in storage.getRequests(RequestStatus.NEW)) {
|
|
||||||
createTicket(signingRequest)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
log.warn("There were errors while creating Jira tickets", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createTicket(signingRequest: CertificateSigningRequest) {
|
|
||||||
jiraClient.createRequestTicket(signingRequest.requestId, signingRequest.request)
|
|
||||||
storage.markRequestTicketCreated(signingRequest.requestId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
package com.r3.corda.networkmanage.doorman.signer
|
||||||
|
|
||||||
|
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
||||||
|
import com.r3.corda.networkmanage.common.persistence.CertificateSigningRequest
|
||||||
|
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage
|
||||||
|
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
||||||
|
import com.r3.corda.networkmanage.doorman.JiraClient
|
||||||
|
import net.corda.core.utilities.contextLogger
|
||||||
|
import net.corda.core.utilities.loggerFor
|
||||||
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
|
|
||||||
|
class JiraCsrHandler(private val jiraClient: JiraClient, private val storage: CertificationRequestStorage, private val delegate: CsrHandler) : CsrHandler by delegate {
|
||||||
|
private companion object {
|
||||||
|
val log = contextLogger()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveRequest(rawRequest: PKCS10CertificationRequest): String {
|
||||||
|
val requestId = delegate.saveRequest(rawRequest)
|
||||||
|
// Make sure request has been accepted.
|
||||||
|
try {
|
||||||
|
if (delegate.getResponse(requestId) !is CertificateResponse.Unauthorised) {
|
||||||
|
jiraClient.createRequestTicket(requestId, rawRequest)
|
||||||
|
storage.markRequestTicketCreated(requestId)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
log.warn("There was an error while creating Jira tickets", e)
|
||||||
|
} finally {
|
||||||
|
return requestId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun processApprovedRequests() {
|
||||||
|
val approvedRequest = jiraClient.getApprovedRequests()
|
||||||
|
approvedRequest.forEach { (id, approvedBy) -> storage.approveRequest(id, approvedBy) }
|
||||||
|
delegate.processApprovedRequests()
|
||||||
|
|
||||||
|
val signedRequests = approvedRequest.mapNotNull { (id, _) ->
|
||||||
|
val request = storage.getRequest(id)
|
||||||
|
|
||||||
|
if (request != null && request.status == RequestStatus.SIGNED) {
|
||||||
|
request.certData?.certPath?.let { certs -> id to certs }
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}.toMap()
|
||||||
|
jiraClient.updateSignedRequests(signedRequests)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates Jira tickets for all request in [RequestStatus.NEW] state.
|
||||||
|
*
|
||||||
|
* Usually requests are expected to move to the [RequestStatus.TICKET_CREATED] state immediately,
|
||||||
|
* they might be left in the [RequestStatus.NEW] state if Jira is down.
|
||||||
|
*/
|
||||||
|
override fun createTickets() {
|
||||||
|
try {
|
||||||
|
for (signingRequest in storage.getRequests(RequestStatus.NEW)) {
|
||||||
|
createTicket(signingRequest)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
log.warn("There were errors while creating Jira tickets", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createTicket(signingRequest: CertificateSigningRequest) {
|
||||||
|
jiraClient.createRequestTicket(signingRequest.requestId, signingRequest.request)
|
||||||
|
storage.markRequestTicketCreated(signingRequest.requestId)
|
||||||
|
}
|
||||||
|
}
|
@ -1,48 +1,20 @@
|
|||||||
package com.r3.corda.networkmanage.doorman.signer
|
package com.r3.corda.networkmanage.doorman.signer
|
||||||
|
|
||||||
import com.r3.corda.networkmanage.common.signer.Signer
|
import com.r3.corda.networkmanage.common.signer.Signer
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.internal.DigitalSignatureWithCert
|
import net.corda.core.internal.DigitalSignatureWithCert
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import java.security.KeyStore
|
||||||
import org.bouncycastle.asn1.x509.GeneralName
|
import java.security.PrivateKey
|
||||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
|
||||||
import org.bouncycastle.asn1.x509.NameConstraints
|
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
|
||||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
|
||||||
import java.security.KeyPair
|
|
||||||
import java.security.cert.CertPath
|
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import javax.security.auth.x500.X500Principal
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The [LocalSigner] class signs [PKCS10CertificationRequest] using provided CA key pair and certificate path.
|
* This local signer is intended to be used in testing environment where hardware signing module is not available.
|
||||||
* This is intended to be used in testing environment where hardware signing module is not available.
|
|
||||||
*/
|
*/
|
||||||
//TODO Use a list instead of array
|
class LocalSigner(private val signingKey: PrivateKey, private val signingCert: X509Certificate) : Signer {
|
||||||
class LocalSigner(private val signingKeyPair: KeyPair, private val signingCertPath: Array<X509Certificate>) : Signer {
|
constructor(certAndKeyPair: CertificateAndKeyPair) : this(certAndKeyPair.keyPair.private, certAndKeyPair.certificate)
|
||||||
// TODO This doesn't belong in this class
|
|
||||||
fun createSignedClientCertificate(certificationRequest: PKCS10CertificationRequest): CertPath {
|
|
||||||
// The sub certs issued by the client must satisfy this directory name (or legal name in Corda) constraints, sub certs' directory name must be within client CA's name's subtree,
|
|
||||||
// please see [sun.security.x509.X500Name.isWithinSubtree()] for more information.
|
|
||||||
// We assume all attributes in the subject name has been checked prior approval.
|
|
||||||
// TODO: add validation to subject name.
|
|
||||||
val request = JcaPKCS10CertificationRequest(certificationRequest)
|
|
||||||
val nameConstraints = NameConstraints(
|
|
||||||
arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, request.subject))),
|
|
||||||
arrayOf())
|
|
||||||
val nodeCaCert = X509Utilities.createCertificate(
|
|
||||||
CertificateType.NODE_CA,
|
|
||||||
signingCertPath[0],
|
|
||||||
signingKeyPair,
|
|
||||||
X500Principal(request.subject.encoded),
|
|
||||||
request.publicKey,
|
|
||||||
nameConstraints = nameConstraints)
|
|
||||||
return buildCertPath(nodeCaCert, *signingCertPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun signBytes(data: ByteArray): DigitalSignatureWithCert {
|
override fun signBytes(data: ByteArray): DigitalSignatureWithCert {
|
||||||
return DigitalSignatureWithCert(signingCertPath[0], Crypto.doSign(signingKeyPair.private, data))
|
return DigitalSignatureWithCert(signingCert, Crypto.doSign(signingKey, data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,8 @@ import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
|||||||
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
|
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
|
||||||
import com.r3.corda.networkmanage.common.persistence.NodeInfoWithSigned
|
import com.r3.corda.networkmanage.common.persistence.NodeInfoWithSigned
|
||||||
import com.r3.corda.networkmanage.common.utils.SignedNetworkMap
|
import com.r3.corda.networkmanage.common.utils.SignedNetworkMap
|
||||||
import com.r3.corda.networkmanage.common.utils.SignedNetworkParameters
|
|
||||||
import com.r3.corda.networkmanage.doorman.NetworkMapConfig
|
import com.r3.corda.networkmanage.doorman.NetworkMapConfig
|
||||||
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService.Companion.NETWORK_MAP_PATH
|
import com.r3.corda.networkmanage.doorman.webservice.NetworkMapWebService.Companion.NETWORK_MAP_PATH
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
@ -31,9 +30,9 @@ import javax.ws.rs.core.Response.ok
|
|||||||
import javax.ws.rs.core.Response.status
|
import javax.ws.rs.core.Response.status
|
||||||
|
|
||||||
@Path(NETWORK_MAP_PATH)
|
@Path(NETWORK_MAP_PATH)
|
||||||
class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage,
|
class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage,
|
||||||
private val networkMapStorage: NetworkMapStorage,
|
private val networkMapStorage: NetworkMapStorage,
|
||||||
private val config: NetworkMapConfig) {
|
private val config: NetworkMapConfig) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val log = contextLogger()
|
val log = contextLogger()
|
||||||
@ -42,7 +41,7 @@ class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage,
|
|||||||
|
|
||||||
private val networkMapCache: LoadingCache<Boolean, Pair<SignedNetworkMap?, NetworkParameters?>> = CacheBuilder.newBuilder()
|
private val networkMapCache: LoadingCache<Boolean, Pair<SignedNetworkMap?, NetworkParameters?>> = CacheBuilder.newBuilder()
|
||||||
.expireAfterWrite(config.cacheTimeout, TimeUnit.MILLISECONDS)
|
.expireAfterWrite(config.cacheTimeout, TimeUnit.MILLISECONDS)
|
||||||
.build(CacheLoader.from { _ -> Pair(networkMapStorage.getCurrentNetworkMap(), networkMapStorage.getCurrentSignedNetworkParameters()?.verified()) })
|
.build(CacheLoader.from { _ -> Pair(networkMapStorage.getCurrentNetworkMap(), networkMapStorage.getNetworkParametersOfNetworkMap()?.verified()) })
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("publish")
|
@Path("publish")
|
@ -1,7 +1,10 @@
|
|||||||
package com.r3.corda.networkmanage.hsm
|
package com.r3.corda.networkmanage.hsm
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.MoreExecutors
|
||||||
|
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
||||||
import com.r3.corda.networkmanage.common.persistence.PersistentNetworkMapStorage
|
import com.r3.corda.networkmanage.common.persistence.PersistentNetworkMapStorage
|
||||||
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
import com.r3.corda.networkmanage.common.persistence.configureDatabase
|
||||||
|
import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
|
||||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||||
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
|
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
|
||||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||||
@ -16,10 +19,19 @@ import com.r3.corda.networkmanage.hsm.persistence.DBSignedCertificateRequestStor
|
|||||||
import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner
|
import com.r3.corda.networkmanage.hsm.signer.HsmCsrSigner
|
||||||
import com.r3.corda.networkmanage.hsm.signer.HsmNetworkMapSigner
|
import com.r3.corda.networkmanage.hsm.signer.HsmNetworkMapSigner
|
||||||
import com.r3.corda.networkmanage.hsm.utils.mapCryptoServerException
|
import com.r3.corda.networkmanage.hsm.utils.mapCryptoServerException
|
||||||
|
import net.corda.core.utilities.minutes
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||||
import java.security.Security
|
import java.security.Security
|
||||||
|
import java.time.Duration
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.ScheduledExecutorService
|
||||||
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
|
import java.util.concurrent.TimeUnit.SECONDS
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
|
|
||||||
|
private val log = LogManager.getLogger("com.r3.corda.networkmanage.hsm.Main")
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
// Grabbed from https://stackoverflow.com/questions/7953567/checking-if-unlimited-cryptography-is-available
|
// Grabbed from https://stackoverflow.com/questions/7953567/checking-if-unlimited-cryptography-is-available
|
||||||
if (Cipher.getMaxAllowedKeyLength("AES") < 256) {
|
if (Cipher.getMaxAllowedKeyLength("AES") < 256) {
|
||||||
@ -46,12 +58,15 @@ fun run(parameters: Parameters) {
|
|||||||
checkNotNull(dataSourceProperties)
|
checkNotNull(dataSourceProperties)
|
||||||
val database = configureDatabase(dataSourceProperties, databaseConfig)
|
val database = configureDatabase(dataSourceProperties, databaseConfig)
|
||||||
val csrStorage = DBSignedCertificateRequestStorage(database)
|
val csrStorage = DBSignedCertificateRequestStorage(database)
|
||||||
val networkMapStorage = PersistentNetworkMapStorage(database)
|
val hsmSigner = HsmNetworkMapSigner(
|
||||||
val hsmNetworkMapSigningThread = HsmNetworkMapSigner(
|
|
||||||
networkMapStorage,
|
|
||||||
networkMapCertificateName,
|
networkMapCertificateName,
|
||||||
networkMapPrivateKeyPassword,
|
networkMapPrivateKeyPassword,
|
||||||
Authenticator(createProvider(), AuthMode.KEY_FILE, autoUsername, authKeyFilePath, authKeyFilePassword, signAuthThreshold)).start()
|
Authenticator(createProvider(), AuthMode.KEY_FILE, autoUsername, authKeyFilePath, authKeyFilePassword, signAuthThreshold))
|
||||||
|
|
||||||
|
val networkMapStorage = PersistentNetworkMapStorage(database)
|
||||||
|
val scheduler = Executors.newSingleThreadScheduledExecutor()
|
||||||
|
startNetworkingMapSigningPolling(networkMapStorage, hsmSigner, scheduler, 10.minutes)
|
||||||
|
|
||||||
val sign: (List<ApprovedCertificateRequestData>) -> Unit = {
|
val sign: (List<ApprovedCertificateRequestData>) -> Unit = {
|
||||||
val signer = HsmCsrSigner(
|
val signer = HsmCsrSigner(
|
||||||
csrStorage,
|
csrStorage,
|
||||||
@ -62,6 +77,7 @@ fun run(parameters: Parameters) {
|
|||||||
Authenticator(createProvider(), authMode, autoUsername, authKeyFilePath, authKeyFilePassword, signAuthThreshold))
|
Authenticator(createProvider(), authMode, autoUsername, authKeyFilePath, authKeyFilePassword, signAuthThreshold))
|
||||||
signer.sign(it)
|
signer.sign(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu().withExceptionHandler(::processError).addItem("1", "Generate root and intermediate certificates", {
|
Menu().withExceptionHandler(::processError).addItem("1", "Generate root and intermediate certificates", {
|
||||||
if (confirmedKeyGen()) {
|
if (confirmedKeyGen()) {
|
||||||
val generator = KeyCertificateGenerator(
|
val generator = KeyCertificateGenerator(
|
||||||
@ -104,10 +120,25 @@ fun run(parameters: Parameters) {
|
|||||||
println("There is no approved and unsigned CSR")
|
println("There is no approved and unsigned CSR")
|
||||||
}
|
}
|
||||||
}).showMenu()
|
}).showMenu()
|
||||||
hsmNetworkMapSigningThread.stop()
|
|
||||||
|
MoreExecutors.shutdownAndAwaitTermination(scheduler, 30, SECONDS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startNetworkingMapSigningPolling(networkMapStorage: NetworkMapStorage,
|
||||||
|
signer: HsmNetworkMapSigner,
|
||||||
|
executor: ScheduledExecutorService,
|
||||||
|
signingPeriod: Duration) {
|
||||||
|
val networkMapSigner = NetworkMapSigner(networkMapStorage, signer)
|
||||||
|
executor.scheduleAtFixedRate({
|
||||||
|
try {
|
||||||
|
networkMapSigner.signNetworkMap()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
log.warn("Exception thrown while signing network map", e)
|
||||||
|
}
|
||||||
|
}, signingPeriod.toMillis(), signingPeriod.toMillis(), MILLISECONDS)
|
||||||
|
}
|
||||||
|
|
||||||
private fun processError(exception: Exception) {
|
private fun processError(exception: Exception) {
|
||||||
val processed = mapCryptoServerException(exception)
|
val processed = mapCryptoServerException(exception)
|
||||||
System.err.println("An error occurred: ${processed.message}")
|
System.err.println("An error occurred: ${processed.message}")
|
||||||
|
@ -1,78 +1,39 @@
|
|||||||
package com.r3.corda.networkmanage.hsm.signer
|
package com.r3.corda.networkmanage.hsm.signer
|
||||||
|
|
||||||
import com.google.common.util.concurrent.MoreExecutors
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
|
||||||
import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
|
|
||||||
import com.r3.corda.networkmanage.common.signer.Signer
|
import com.r3.corda.networkmanage.common.signer.Signer
|
||||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||||
import com.r3.corda.networkmanage.hsm.utils.X509Utilities
|
import com.r3.corda.networkmanage.hsm.utils.X509Utilities
|
||||||
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.getAndInitializeKeyStore
|
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.getAndInitializeKeyStore
|
||||||
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.verify
|
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.verify
|
||||||
import net.corda.core.internal.DigitalSignatureWithCert
|
import net.corda.core.internal.DigitalSignatureWithCert
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.nodeapi.internal.crypto.getX509Certificate
|
||||||
import net.corda.core.utilities.minutes
|
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
import java.security.Signature
|
import java.security.Signature
|
||||||
import java.security.cert.X509Certificate
|
|
||||||
import java.time.Duration
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
import java.util.concurrent.ScheduledExecutorService
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates logic for periodic network map signing execution.
|
* Signer which connects to a HSM using the given [authenticator] to sign bytes.
|
||||||
* It uses HSM as the signing entity with keys and certificates specified at the construction time.
|
|
||||||
*/
|
*/
|
||||||
// TODO Rename this to HsmSigner
|
// TODO Rename this to HsmSigner
|
||||||
class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage,
|
class HsmNetworkMapSigner(private val certificateKeyName: String,
|
||||||
private val caCertificateKeyName: String,
|
private val privateKeyPassword: String,
|
||||||
private val caPrivateKeyPass: String,
|
private val authenticator: Authenticator) : Signer {
|
||||||
private val authenticator: Authenticator,
|
|
||||||
private val signingPeriod: Duration = DEFAULT_SIGNING_PERIOD_MS) : Signer {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val log = loggerFor<HsmNetworkMapSigner>()
|
|
||||||
val DEFAULT_SIGNING_PERIOD_MS = 10.minutes
|
|
||||||
|
|
||||||
private val TERMINATION_TIMEOUT_SEC = 2L
|
|
||||||
}
|
|
||||||
|
|
||||||
private val networkMapSigner = NetworkMapSigner(networkMapStorage, this)
|
|
||||||
private lateinit var scheduledExecutor: ScheduledExecutorService
|
|
||||||
|
|
||||||
// TODO This doesn't belong in this class
|
|
||||||
fun start(): HsmNetworkMapSigner {
|
|
||||||
val signingPeriodMillis = signingPeriod.toMillis()
|
|
||||||
scheduledExecutor = Executors.newSingleThreadScheduledExecutor()
|
|
||||||
scheduledExecutor.scheduleAtFixedRate({
|
|
||||||
try {
|
|
||||||
networkMapSigner.signNetworkMap()
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
log.warn("Exception thrown while signing network map", exception)
|
|
||||||
}
|
|
||||||
}, signingPeriodMillis, signingPeriodMillis, TimeUnit.MILLISECONDS)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stop() {
|
|
||||||
MoreExecutors.shutdownAndAwaitTermination(scheduledExecutor, TERMINATION_TIMEOUT_SEC, TimeUnit.SECONDS)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs given data using [CryptoServerJCE.CryptoServerProvider], which connects to the underlying HSM.
|
* Signs given data using [CryptoServerJCE.CryptoServerProvider], which connects to the underlying HSM.
|
||||||
*/
|
*/
|
||||||
override fun signBytes(data: ByteArray): DigitalSignatureWithCert {
|
override fun signBytes(data: ByteArray): DigitalSignatureWithCert {
|
||||||
return authenticator.connectAndAuthenticate { provider, _ ->
|
return authenticator.connectAndAuthenticate { provider, _ ->
|
||||||
val keyStore = getAndInitializeKeyStore(provider)
|
val keyStore = getAndInitializeKeyStore(provider)
|
||||||
val caCertificateChain = keyStore.getCertificateChain(caCertificateKeyName)
|
val certificate = keyStore.getX509Certificate(certificateKeyName)
|
||||||
val caKey = keyStore.getKey(caCertificateKeyName, caPrivateKeyPass.toCharArray()) as PrivateKey
|
// Don't worry this is not a real private key but a pointer to one that resides in the HSM. It only works
|
||||||
|
// when used with the given provider.
|
||||||
|
val key = keyStore.getKey(certificateKeyName, privateKeyPassword.toCharArray()) as PrivateKey
|
||||||
val signature = Signature.getInstance(X509Utilities.SIGNATURE_ALGORITHM, provider).run {
|
val signature = Signature.getInstance(X509Utilities.SIGNATURE_ALGORITHM, provider).run {
|
||||||
initSign(caKey)
|
initSign(key)
|
||||||
update(data)
|
update(data)
|
||||||
sign()
|
sign()
|
||||||
}
|
}
|
||||||
verify(data, signature, caCertificateChain[0].publicKey)
|
verify(data, signature, certificate.publicKey)
|
||||||
DigitalSignatureWithCert(caCertificateChain[0] as X509Certificate, signature)
|
DigitalSignatureWithCert(certificate, signature)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
||||||
val persistedSignedParameters = networkMapStorage.getCurrentSignedNetworkParameters()
|
val persistedSignedParameters = networkMapStorage.getNetworkParametersOfNetworkMap()
|
||||||
|
|
||||||
assertEquals(networkParameters, persistedSignedParameters?.verifiedNetworkMapCert(rootCaCert))
|
assertEquals(networkParameters, persistedSignedParameters?.verifiedNetworkMapCert(rootCaCert))
|
||||||
assertEquals(parametersSignature, persistedSignedParameters?.sig)
|
assertEquals(parametersSignature, persistedSignedParameters?.sig)
|
||||||
@ -84,13 +84,13 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
|||||||
networkMapStorage.saveNetworkParameters(params2, null)
|
networkMapStorage.saveNetworkParameters(params2, null)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val latest = networkMapStorage.getLatestUnsignedNetworkParameters()
|
val latest = networkMapStorage.getLatestNetworkParameters()?.minimumPlatformVersion
|
||||||
// then
|
// then
|
||||||
assertEquals(2, latest.minimumPlatformVersion)
|
assertEquals(2, latest)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `getCurrentNetworkParameters returns current network map parameters`() {
|
fun `getNetworkParametersOfNetworkMap returns current network map parameters`() {
|
||||||
// given
|
// given
|
||||||
// Create network parameters
|
// Create network parameters
|
||||||
val testParameters1 = testNetworkParameters(emptyList())
|
val testParameters1 = testNetworkParameters(emptyList())
|
||||||
@ -107,7 +107,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
|||||||
networkMapStorage.saveNetworkParameters(testParameters2, testParameters2.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate).sig)
|
networkMapStorage.saveNetworkParameters(testParameters2, testParameters2.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate).sig)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val result = networkMapStorage.getCurrentSignedNetworkParameters()?.verifiedNetworkMapCert(rootCaCert)
|
val result = networkMapStorage.getNetworkParametersOfNetworkMap()?.verifiedNetworkMapCert(rootCaCert)
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(1, result?.minimumPlatformVersion)
|
assertEquals(1, result?.minimumPlatformVersion)
|
||||||
|
@ -51,8 +51,8 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
|
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
|
||||||
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
||||||
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes)
|
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes)
|
||||||
whenever(networkMapStorage.getLatestUnsignedNetworkParameters()).thenReturn(latestNetworkParameters)
|
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(latestNetworkParameters)
|
||||||
whenever(networkMapStorage.getCurrentSignedNetworkParameters()).thenReturn(currentParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
whenever(networkMapStorage.getNetworkParametersOfNetworkMap()).thenReturn(currentParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
||||||
whenever(signer.signBytes(any())).then {
|
whenever(signer.signBytes(any())).then {
|
||||||
DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray))
|
DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray))
|
||||||
}
|
}
|
||||||
@ -67,8 +67,8 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
// then
|
// then
|
||||||
// Verify networkMapStorage calls
|
// Verify networkMapStorage calls
|
||||||
verify(networkMapStorage).getNodeInfoHashes(any())
|
verify(networkMapStorage).getNodeInfoHashes(any())
|
||||||
verify(networkMapStorage).getLatestUnsignedNetworkParameters()
|
verify(networkMapStorage).getLatestNetworkParameters()
|
||||||
verify(networkMapStorage).getCurrentSignedNetworkParameters()
|
verify(networkMapStorage).getNetworkParametersOfNetworkMap()
|
||||||
argumentCaptor<SignedNetworkMap>().apply {
|
argumentCaptor<SignedNetworkMap>().apply {
|
||||||
verify(networkMapStorage).saveNetworkMap(capture())
|
verify(networkMapStorage).saveNetworkMap(capture())
|
||||||
val capturedNetworkMap = firstValue.verifiedNetworkMapCert(rootCaCert)
|
val capturedNetworkMap = firstValue.verifiedNetworkMapCert(rootCaCert)
|
||||||
@ -87,8 +87,8 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
|
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
|
||||||
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
||||||
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
|
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
|
||||||
whenever(networkMapStorage.getLatestUnsignedNetworkParameters()).thenReturn(networkParameters)
|
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
||||||
whenever(networkMapStorage.getCurrentSignedNetworkParameters()).thenReturn(networkParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
whenever(networkMapStorage.getNetworkParametersOfNetworkMap()).thenReturn(networkParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
||||||
|
|
||||||
// when
|
// when
|
||||||
networkMapSigner.signNetworkMap()
|
networkMapSigner.signNetworkMap()
|
||||||
@ -104,7 +104,7 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
val networkParameters = testNetworkParameters(emptyList())
|
val networkParameters = testNetworkParameters(emptyList())
|
||||||
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(null)
|
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(null)
|
||||||
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
|
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
|
||||||
whenever(networkMapStorage.getLatestUnsignedNetworkParameters()).thenReturn(networkParameters)
|
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
||||||
whenever(signer.signBytes(any())).then {
|
whenever(signer.signBytes(any())).then {
|
||||||
DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray))
|
DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray))
|
||||||
}
|
}
|
||||||
@ -118,7 +118,7 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
// then
|
// then
|
||||||
// Verify networkMapStorage calls
|
// Verify networkMapStorage calls
|
||||||
verify(networkMapStorage).getNodeInfoHashes(any())
|
verify(networkMapStorage).getNodeInfoHashes(any())
|
||||||
verify(networkMapStorage).getLatestUnsignedNetworkParameters()
|
verify(networkMapStorage).getLatestNetworkParameters()
|
||||||
argumentCaptor<SignedNetworkMap>().apply {
|
argumentCaptor<SignedNetworkMap>().apply {
|
||||||
verify(networkMapStorage).saveNetworkMap(capture())
|
verify(networkMapStorage).saveNetworkMap(capture())
|
||||||
val networkMap = firstValue.verifiedNetworkMapCert(rootCaCert)
|
val networkMap = firstValue.verifiedNetworkMapCert(rootCaCert)
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
package com.r3.corda.networkmanage.doorman
|
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.any
|
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
|
||||||
import com.nhaarman.mockito_kotlin.times
|
|
||||||
import com.nhaarman.mockito_kotlin.verify
|
|
||||||
import com.r3.corda.networkmanage.TestBase
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.CertificateStatus
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
|
||||||
import com.r3.corda.networkmanage.doorman.signer.DefaultCsrHandler
|
|
||||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
|
||||||
import net.corda.core.crypto.Crypto
|
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
|
||||||
import org.junit.Test
|
|
||||||
import javax.security.auth.x500.X500Principal
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
class DefaultRequestProcessorTest : TestBase() {
|
|
||||||
@Test
|
|
||||||
fun `get response`() {
|
|
||||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
val cert = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Test,L=London,C=GB"), keyPair)
|
|
||||||
|
|
||||||
val requestStorage: CertificationRequestStorage = mock {
|
|
||||||
on { getRequest("New") }.thenReturn(certificateSigningRequest())
|
|
||||||
on { getRequest("Signed") }.thenReturn(certificateSigningRequest(status = RequestStatus.SIGNED, certData = certificateData("", CertificateStatus.VALID, buildCertPath(cert))))
|
|
||||||
on { getRequest("Rejected") }.thenReturn(certificateSigningRequest(status = RequestStatus.REJECTED, remark = "Random reason"))
|
|
||||||
}
|
|
||||||
val signer: LocalSigner = mock()
|
|
||||||
val requestProcessor = DefaultCsrHandler(requestStorage, signer)
|
|
||||||
|
|
||||||
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("random"))
|
|
||||||
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("New"))
|
|
||||||
assertEquals(CertificateResponse.Ready(buildCertPath(cert)), requestProcessor.getResponse("Signed"))
|
|
||||||
assertEquals(CertificateResponse.Unauthorised("Random reason"), requestProcessor.getResponse("Rejected"))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `process request`() {
|
|
||||||
val (request1, request2, request3) = (1..3).map {
|
|
||||||
X509Utilities.createCertificateSigningRequest(X500Principal("O=Test1,L=London,C=GB"), "my@email.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
|
||||||
}
|
|
||||||
|
|
||||||
val requestStorage: CertificationRequestStorage = mock {
|
|
||||||
on { getRequests(RequestStatus.APPROVED) }.thenReturn(listOf(
|
|
||||||
certificateSigningRequest(requestId = "1", request = request1, status = RequestStatus.APPROVED),
|
|
||||||
certificateSigningRequest(requestId = "2", request = request2, status = RequestStatus.APPROVED),
|
|
||||||
certificateSigningRequest(requestId = "3", request = request3, status = RequestStatus.APPROVED)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
val signer: LocalSigner = mock()
|
|
||||||
val requestProcessor = DefaultCsrHandler(requestStorage, signer)
|
|
||||||
|
|
||||||
requestProcessor.processApprovedRequests()
|
|
||||||
|
|
||||||
verify(signer, times(3)).createSignedClientCertificate(any())
|
|
||||||
verify(requestStorage, times(1)).getRequests(any())
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,85 @@
|
|||||||
|
package com.r3.corda.networkmanage.doorman.signer
|
||||||
|
|
||||||
|
import com.nhaarman.mockito_kotlin.*
|
||||||
|
import com.r3.corda.networkmanage.TestBase
|
||||||
|
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
||||||
|
import com.r3.corda.networkmanage.common.persistence.CertificateStatus
|
||||||
|
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage
|
||||||
|
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
||||||
|
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
||||||
|
import com.r3.corda.networkmanage.common.utils.CertPathAndKey
|
||||||
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
|
import net.corda.core.crypto.Crypto
|
||||||
|
import net.corda.core.internal.CertRole
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.Test
|
||||||
|
import java.security.cert.CertPath
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class DefaultCsrHandlerTest : TestBase() {
|
||||||
|
@Test
|
||||||
|
fun getResponse() {
|
||||||
|
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val cert = X509Utilities.createSelfSignedCACertificate(X500Principal("O=Test,L=London,C=GB"), keyPair)
|
||||||
|
|
||||||
|
val requestStorage: CertificationRequestStorage = mock {
|
||||||
|
on { getRequest("New") }.thenReturn(certificateSigningRequest())
|
||||||
|
on { getRequest("Signed") }.thenReturn(certificateSigningRequest(status = RequestStatus.SIGNED, certData = certificateData("", CertificateStatus.VALID, buildCertPath(cert))))
|
||||||
|
on { getRequest("Rejected") }.thenReturn(certificateSigningRequest(status = RequestStatus.REJECTED, remark = "Random reason"))
|
||||||
|
}
|
||||||
|
val requestProcessor = DefaultCsrHandler(requestStorage, null)
|
||||||
|
|
||||||
|
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("random"))
|
||||||
|
assertEquals(CertificateResponse.NotReady, requestProcessor.getResponse("New"))
|
||||||
|
assertEquals(CertificateResponse.Ready(buildCertPath(cert)), requestProcessor.getResponse("Signed"))
|
||||||
|
assertEquals(CertificateResponse.Unauthorised("Random reason"), requestProcessor.getResponse("Rejected"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun processApprovedRequests() {
|
||||||
|
val requests = (1..3).map {
|
||||||
|
X509Utilities.createCertificateSigningRequest(
|
||||||
|
X500Principal("O=Test$it,L=London,C=GB"),
|
||||||
|
"my@email.com",
|
||||||
|
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
|
||||||
|
}
|
||||||
|
|
||||||
|
val requestStorage: CertificationRequestStorage = mock {
|
||||||
|
on { getRequests(RequestStatus.APPROVED) }.thenReturn(listOf(
|
||||||
|
certificateSigningRequest(requestId = "1", request = requests[0], status = RequestStatus.APPROVED),
|
||||||
|
certificateSigningRequest(requestId = "2", request = requests[1], status = RequestStatus.APPROVED)
|
||||||
|
))
|
||||||
|
on { getRequests(RequestStatus.REJECTED) }.thenReturn(listOf(
|
||||||
|
certificateSigningRequest(requestId = "3", request = requests[2], status = RequestStatus.REJECTED)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
val (rootCa, csrCa) = createDevIntermediateCaCertPath()
|
||||||
|
val csrCertPathAndKey = CertPathAndKey(listOf(csrCa.certificate, rootCa.certificate), csrCa.keyPair.private)
|
||||||
|
val requestProcessor = DefaultCsrHandler(requestStorage, csrCertPathAndKey)
|
||||||
|
|
||||||
|
requestProcessor.processApprovedRequests()
|
||||||
|
|
||||||
|
val certPathCapture = argumentCaptor<CertPath>()
|
||||||
|
|
||||||
|
// Verify only the approved requests are taken
|
||||||
|
verify(requestStorage, times(1)).getRequests(RequestStatus.APPROVED)
|
||||||
|
verify(requestStorage, times(1)).putCertificatePath(eq("1"), certPathCapture.capture(), eq(listOf(DOORMAN_SIGNATURE)))
|
||||||
|
verify(requestStorage, times(1)).putCertificatePath(eq("2"), certPathCapture.capture(), eq(listOf(DOORMAN_SIGNATURE)))
|
||||||
|
|
||||||
|
// Then make sure the generated node cert paths are correct
|
||||||
|
certPathCapture.allValues.forEachIndexed { index, certPath ->
|
||||||
|
X509Utilities.validateCertificateChain(rootCa.certificate, *certPath.certificates.toTypedArray())
|
||||||
|
assertThat(certPath.certificates).hasSize(3).element(1).isEqualTo(csrCa.certificate)
|
||||||
|
(certPath.certificates[0] as X509Certificate).apply {
|
||||||
|
assertThat(CertRole.extract(this)).isEqualTo(CertRole.NODE_CA)
|
||||||
|
assertThat(publicKey).isEqualTo(Crypto.toSupportedPublicKey(requests[index].subjectPublicKeyInfo))
|
||||||
|
assertThat(subjectX500Principal).isEqualTo(X500Principal("O=Test${index + 1},L=London,C=GB"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,25 +14,26 @@ import org.junit.Rule
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.junit.MockitoJUnit
|
import org.mockito.junit.MockitoJUnit
|
||||||
|
import org.mockito.junit.MockitoRule
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
|
|
||||||
class JiraCsrHandlerTest {
|
class JiraCsrHandlerTest {
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val mockitoRule = MockitoJUnit.rule()
|
val mockitoRule: MockitoRule = MockitoJUnit.rule()
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
lateinit var jiraClient: JiraClient
|
private lateinit var jiraClient: JiraClient
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
lateinit var certificationRequestStorage: CertificationRequestStorage
|
private lateinit var certificationRequestStorage: CertificationRequestStorage
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
lateinit var defaultCsrHandler: DefaultCsrHandler
|
private lateinit var defaultCsrHandler: DefaultCsrHandler
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
var certPath: CertPath = mock()
|
private val certPath: CertPath = mock()
|
||||||
|
|
||||||
private lateinit var jiraCsrHandler: JiraCsrHandler
|
private lateinit var jiraCsrHandler: JiraCsrHandler
|
||||||
private val requestId = "id"
|
private val requestId = "id"
|
@ -1,4 +1,4 @@
|
|||||||
package com.r3.corda.networkmanage.doorman
|
package com.r3.corda.networkmanage.doorman.webservice
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import com.nhaarman.mockito_kotlin.mock
|
||||||
import com.nhaarman.mockito_kotlin.times
|
import com.nhaarman.mockito_kotlin.times
|
||||||
@ -7,7 +7,8 @@ import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
|||||||
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
|
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
|
||||||
import com.r3.corda.networkmanage.common.utils.SignedNetworkMap
|
import com.r3.corda.networkmanage.common.utils.SignedNetworkMap
|
||||||
import com.r3.corda.networkmanage.common.utils.SignedNetworkParameters
|
import com.r3.corda.networkmanage.common.utils.SignedNetworkParameters
|
||||||
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService
|
import com.r3.corda.networkmanage.doorman.NetworkManagementWebServer
|
||||||
|
import com.r3.corda.networkmanage.doorman.NetworkMapConfig
|
||||||
import net.corda.core.crypto.SecureHash.Companion.randomSHA256
|
import net.corda.core.crypto.SecureHash.Companion.randomSHA256
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.checkOkResponse
|
import net.corda.core.internal.checkOkResponse
|
||||||
@ -36,7 +37,7 @@ import java.security.cert.X509Certificate
|
|||||||
import javax.ws.rs.core.MediaType
|
import javax.ws.rs.core.MediaType
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class NodeInfoWebServiceTest {
|
class NetworkMapWebServiceTest {
|
||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val testSerialization = SerializationEnvironmentRule(true)
|
val testSerialization = SerializationEnvironmentRule(true)
|
||||||
@ -56,12 +57,12 @@ class NodeInfoWebServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `submit nodeInfo`() {
|
fun `submit nodeInfo`() {
|
||||||
val networkMapStorage: NetworkMapStorage = mock {
|
val networkMapStorage: NetworkMapStorage = mock {
|
||||||
on { getCurrentSignedNetworkParameters() }.thenReturn(testNetworkParameters(emptyList()).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
on { getNetworkParametersOfNetworkMap() }.thenReturn(testNetworkParameters(emptyList()).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
||||||
}
|
}
|
||||||
// Create node info.
|
// Create node info.
|
||||||
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"))
|
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"))
|
||||||
|
|
||||||
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(mock(), networkMapStorage, testNetworkMapConfig)).use {
|
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NetworkMapWebService(mock(), networkMapStorage, testNetworkMapConfig)).use {
|
||||||
it.start()
|
it.start()
|
||||||
val nodeInfoAndSignature = signedNodeInfo.serialize().bytes
|
val nodeInfoAndSignature = signedNodeInfo.serialize().bytes
|
||||||
// Post node info and signature to doorman, this should pass without any exception.
|
// Post node info and signature to doorman, this should pass without any exception.
|
||||||
@ -72,12 +73,12 @@ class NodeInfoWebServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `submit old nodeInfo`() {
|
fun `submit old nodeInfo`() {
|
||||||
val networkMapStorage: NetworkMapStorage = mock {
|
val networkMapStorage: NetworkMapStorage = mock {
|
||||||
on { getCurrentSignedNetworkParameters() }.thenReturn(testNetworkParameters(emptyList(), minimumPlatformVersion = 2).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
on { getNetworkParametersOfNetworkMap() }.thenReturn(testNetworkParameters(emptyList(), minimumPlatformVersion = 2).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
||||||
}
|
}
|
||||||
// Create node info.
|
// Create node info.
|
||||||
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"), platformVersion = 1)
|
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"), platformVersion = 1)
|
||||||
|
|
||||||
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(mock(), networkMapStorage, testNetworkMapConfig)).use {
|
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NetworkMapWebService(mock(), networkMapStorage, testNetworkMapConfig)).use {
|
||||||
it.start()
|
it.start()
|
||||||
val nodeInfoAndSignature = signedNodeInfo.serialize().bytes
|
val nodeInfoAndSignature = signedNodeInfo.serialize().bytes
|
||||||
assertThatThrownBy { it.doPost("publish", nodeInfoAndSignature) }
|
assertThatThrownBy { it.doPost("publish", nodeInfoAndSignature) }
|
||||||
@ -88,12 +89,12 @@ class NodeInfoWebServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `submit nodeInfo when no network parameters`() {
|
fun `submit nodeInfo when no network parameters`() {
|
||||||
val networkMapStorage: NetworkMapStorage = mock {
|
val networkMapStorage: NetworkMapStorage = mock {
|
||||||
on { getCurrentSignedNetworkParameters() }.thenReturn(null)
|
on { getNetworkParametersOfNetworkMap() }.thenReturn(null)
|
||||||
}
|
}
|
||||||
// Create node info.
|
// Create node info.
|
||||||
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"), platformVersion = 1)
|
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"), platformVersion = 1)
|
||||||
|
|
||||||
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(mock(), networkMapStorage, testNetworkMapConfig)).use {
|
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NetworkMapWebService(mock(), networkMapStorage, testNetworkMapConfig)).use {
|
||||||
it.start()
|
it.start()
|
||||||
val nodeInfoAndSignature = signedNodeInfo.serialize().bytes
|
val nodeInfoAndSignature = signedNodeInfo.serialize().bytes
|
||||||
assertThatThrownBy { it.doPost("publish", nodeInfoAndSignature) }
|
assertThatThrownBy { it.doPost("publish", nodeInfoAndSignature) }
|
||||||
@ -110,7 +111,7 @@ class NodeInfoWebServiceTest {
|
|||||||
on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap)
|
on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(mock(), networkMapStorage, testNetworkMapConfig)).use {
|
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NetworkMapWebService(mock(), networkMapStorage, testNetworkMapConfig)).use {
|
||||||
it.start()
|
it.start()
|
||||||
val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("")
|
val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("")
|
||||||
verify(networkMapStorage, times(1)).getCurrentNetworkMap()
|
verify(networkMapStorage, times(1)).getCurrentNetworkMap()
|
||||||
@ -127,7 +128,7 @@ class NodeInfoWebServiceTest {
|
|||||||
on { getNodeInfo(nodeInfoHash) }.thenReturn(signedNodeInfo)
|
on { getNodeInfo(nodeInfoHash) }.thenReturn(signedNodeInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(nodeInfoStorage, mock(), testNetworkMapConfig)).use {
|
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NetworkMapWebService(nodeInfoStorage, mock(), testNetworkMapConfig)).use {
|
||||||
it.start()
|
it.start()
|
||||||
val nodeInfoResponse = it.doGet<SignedNodeInfo>("node-info/$nodeInfoHash")
|
val nodeInfoResponse = it.doGet<SignedNodeInfo>("node-info/$nodeInfoHash")
|
||||||
verify(nodeInfoStorage, times(1)).getNodeInfo(nodeInfoHash)
|
verify(nodeInfoStorage, times(1)).getNodeInfo(nodeInfoHash)
|
||||||
@ -149,7 +150,7 @@ class NodeInfoWebServiceTest {
|
|||||||
on { getSignedNetworkParameters(networkParametersHash) }.thenReturn(signedNetworkParameters)
|
on { getSignedNetworkParameters(networkParametersHash) }.thenReturn(signedNetworkParameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(mock(), networkMapStorage, testNetworkMapConfig)).use {
|
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NetworkMapWebService(mock(), networkMapStorage, testNetworkMapConfig)).use {
|
||||||
it.start()
|
it.start()
|
||||||
val netParamsResponse = it.doGet<SignedNetworkParameters>("network-parameters/$networkParametersHash")
|
val netParamsResponse = it.doGet<SignedNetworkParameters>("network-parameters/$networkParametersHash")
|
||||||
verify(networkMapStorage, times(1)).getSignedNetworkParameters(networkParametersHash)
|
verify(networkMapStorage, times(1)).getSignedNetworkParameters(networkParametersHash)
|
@ -1,11 +1,11 @@
|
|||||||
package com.r3.corda.networkmanage.doorman
|
package com.r3.corda.networkmanage.doorman.webservice
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.*
|
import com.nhaarman.mockito_kotlin.*
|
||||||
import com.r3.corda.networkmanage.TestBase
|
import com.r3.corda.networkmanage.TestBase
|
||||||
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
|
import com.r3.corda.networkmanage.doorman.NetworkManagementWebServer
|
||||||
import com.r3.corda.networkmanage.doorman.signer.CsrHandler
|
import com.r3.corda.networkmanage.doorman.signer.CsrHandler
|
||||||
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
Loading…
Reference in New Issue
Block a user