mirror of
https://github.com/corda/corda.git
synced 2025-01-14 16:59:52 +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
|
||||
|
||||
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 net.corda.cordform.CordformNode
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
@ -141,11 +142,11 @@ class NodeRegistrationTest : IntegrationTest() {
|
||||
start(
|
||||
serverAddress,
|
||||
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),
|
||||
networkParameters?.let {
|
||||
NetworkMapStartParams(
|
||||
LocalSigner(networkMapCa.keyPair, arrayOf(networkMapCa.certificate, rootCaCert)),
|
||||
LocalSigner(networkMapCa),
|
||||
networkParameters,
|
||||
NetworkMapConfig(cacheTimeout = timeoutMillis, signInterval = timeoutMillis)
|
||||
)
|
||||
|
@ -12,6 +12,7 @@ import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class HsmTest {
|
||||
@ -19,7 +20,12 @@ class HsmTest {
|
||||
@Rule
|
||||
@JvmField
|
||||
val hsmSimulator: HsmSimulator = HsmSimulator()
|
||||
val testParameters = Parameters(
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
private val testParameters = Parameters(
|
||||
dataSourceProperties = mock(),
|
||||
device = "${hsmSimulator.port}@${hsmSimulator.host}",
|
||||
keySpecifier = 1,
|
||||
@ -30,10 +36,6 @@ class HsmTest {
|
||||
validDays = 3650
|
||||
)
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
private lateinit var inputReader: InputReader
|
||||
|
||||
@Before
|
||||
@ -47,14 +49,12 @@ class HsmTest {
|
||||
fun `Authenticator executes the block once user is successfully authenticated`() {
|
||||
// given
|
||||
val authenticator = Authenticator(testParameters.createProvider(), inputReader = inputReader)
|
||||
var executed = false
|
||||
val executed = AtomicBoolean(false)
|
||||
|
||||
// when
|
||||
authenticator.connectAndAuthenticate({ provider, signers ->
|
||||
executed = true
|
||||
})
|
||||
authenticator.connectAndAuthenticate { _, _ -> executed.set(true) }
|
||||
|
||||
// then
|
||||
assertTrue(executed)
|
||||
assertTrue(executed.get())
|
||||
}
|
||||
}
|
@ -90,7 +90,12 @@ class SigningServiceIntegrationTest {
|
||||
val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true))
|
||||
|
||||
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
|
||||
// Start Corda network registration.
|
||||
val config = createConfig().also {
|
||||
|
@ -39,10 +39,9 @@ interface NetworkMapStorage {
|
||||
fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters?
|
||||
|
||||
/**
|
||||
* Retrieve network map parameters.
|
||||
* @return signed current network map parameters or null if they don't exist
|
||||
* Retrieve the network parameters of the current network map, or null if there's no network map.
|
||||
*/
|
||||
fun getCurrentSignedNetworkParameters(): SignedNetworkParameters?
|
||||
fun getNetworkParametersOfNetworkMap(): SignedNetworkParameters?
|
||||
|
||||
/**
|
||||
* Persists given network parameters with signature if provided.
|
||||
@ -55,5 +54,5 @@ interface NetworkMapStorage {
|
||||
* Note that they may not have been signed up yet.
|
||||
* @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.sha256
|
||||
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.nodeapi.internal.network.NetworkMap
|
||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||
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 {
|
||||
override fun getCurrentNetworkMap(): SignedNetworkMap? {
|
||||
return database.transaction {
|
||||
getCurrentNetworkMapEntity()?.let {
|
||||
val signatureAndCertPath = it.signatureAndCertificate()
|
||||
SignedNetworkMap(SerializedBytes(it.networkMap), signatureAndCertPath)
|
||||
}
|
||||
getCurrentNetworkMapEntity()?.toSignedNetworkMap()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCurrentSignedNetworkParameters(): SignedNetworkParameters? {
|
||||
override fun getNetworkParametersOfNetworkMap(): SignedNetworkParameters? {
|
||||
return database.transaction {
|
||||
getCurrentNetworkMapEntity()?.let {
|
||||
val netParamsHash = it.networkMap.deserialize<NetworkMap>().networkParameterHash
|
||||
getSignedNetworkParameters(netParamsHash)
|
||||
val netParamsHash = it.toNetworkMap().networkParameterHash
|
||||
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? {
|
||||
return getNetworkParametersEntity(hash.toString())?.signedParameters()
|
||||
return getNetworkParametersEntity(hash.toString())?.let {
|
||||
if (it.isSigned) it.toSignedNetworkParameters() else null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getNodeInfoHashes(certificateStatus: CertificateStatus): List<SecureHash> {
|
||||
@ -79,18 +76,17 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLatestUnsignedNetworkParameters(): NetworkParameters = getLatestNetworkParametersEntity().networkParameters()
|
||||
|
||||
private fun getLatestNetworkParametersEntity(): NetworkParametersEntity {
|
||||
override fun getLatestNetworkParameters(): NetworkParameters? {
|
||||
return database.transaction {
|
||||
val builder = session.criteriaBuilder
|
||||
val query = builder.createQuery(NetworkParametersEntity::class.java).run {
|
||||
from(NetworkParametersEntity::class.java).run {
|
||||
orderBy(builder.desc(get<String>(NetworkParametersEntity::created.name)))
|
||||
val query = session.criteriaBuilder.run {
|
||||
createQuery(NetworkParametersEntity::class.java).run {
|
||||
from(NetworkParametersEntity::class.java).run {
|
||||
orderBy(desc(get<String>(NetworkParametersEntity::created.name)))
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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
|
||||
|
||||
import com.r3.corda.networkmanage.common.utils.SignedNetworkMap
|
||||
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.network.NetworkMap
|
||||
import javax.persistence.*
|
||||
|
||||
@Entity
|
||||
@ -23,11 +27,12 @@ class NetworkMapEntity(
|
||||
@Column(name = "certificate")
|
||||
val certificate: ByteArray
|
||||
) {
|
||||
/**
|
||||
* Deserializes NetworkMapEntity.signatureBytes into the [DigitalSignatureWithCert] instance
|
||||
*/
|
||||
fun signatureAndCertificate(): DigitalSignatureWithCert {
|
||||
return DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature)
|
||||
}
|
||||
fun toNetworkMap(): NetworkMap = networkMap.deserialize()
|
||||
|
||||
}
|
||||
fun toSignedNetworkMap(): SignedNetworkMap {
|
||||
return SignedNetworkMap(
|
||||
SerializedBytes(networkMap),
|
||||
DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -35,13 +35,15 @@ class NetworkParametersEntity(
|
||||
@Column(name = "certificate")
|
||||
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 signedParameters(): SignedNetworkParameters? {
|
||||
return if (certificate != null && signature != null) {
|
||||
val sigWithCert = DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature)
|
||||
SignedDataWithCert(SerializedBytes(parametersBytes), sigWithCert)
|
||||
} else null
|
||||
fun toNetworkParameters(): NetworkParameters = parametersBytes.deserialize()
|
||||
|
||||
fun toSignedNetworkParameters(): SignedNetworkParameters {
|
||||
if (certificate == null || signature == null) throw IllegalStateException("Network parameters entity is not signed: $parametersHash")
|
||||
return SignedDataWithCert(
|
||||
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 net.corda.core.internal.SignedDataWithCert
|
||||
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.NetworkParameters
|
||||
|
||||
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.
|
||||
*/
|
||||
fun signNetworkMap() {
|
||||
// TODO There is no network parameters update process in place yet. We assume that latest parameters are to be used
|
||||
// in current network map.
|
||||
val latestNetworkParameters = networkMapStorage.getLatestUnsignedNetworkParameters()
|
||||
val currentNetworkParameters = networkMapStorage.getCurrentSignedNetworkParameters()
|
||||
if (currentNetworkParameters?.verified() != latestNetworkParameters)
|
||||
signNetworkParameters(latestNetworkParameters)
|
||||
val latestNetworkParameters = networkMapStorage.getLatestNetworkParameters()
|
||||
if (latestNetworkParameters == null) {
|
||||
logger.debug("No network parameters present")
|
||||
return
|
||||
}
|
||||
val currentNetworkParameters = networkMapStorage.getNetworkParametersOfNetworkMap()
|
||||
if (currentNetworkParameters?.verified() != latestNetworkParameters) {
|
||||
persistSignedNetworkParameters(latestNetworkParameters)
|
||||
}
|
||||
val currentSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
||||
val nodeInfoHashes = networkMapStorage.getNodeInfoHashes(CertificateStatus.VALID)
|
||||
val serialisedNetworkMap = NetworkMap(nodeInfoHashes, latestNetworkParameters.serialize().hash).serialize()
|
||||
@ -27,10 +37,8 @@ class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs latest inserted network parameters.
|
||||
*/
|
||||
fun signNetworkParameters(networkParameters: NetworkParameters) {
|
||||
fun persistSignedNetworkParameters(networkParameters: NetworkParameters) {
|
||||
logger.info("Signing and persisting network parameters: $networkParameters")
|
||||
val digitalSignature = signer.signObject(networkParameters).sig
|
||||
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.network.NetworkMap
|
||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||
import java.security.KeyPair
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
import java.security.cert.CertPath
|
||||
import java.security.cert.Certificate
|
||||
import java.security.cert.X509Certificate
|
||||
|
||||
// TODO These should be defined in node-api
|
||||
typealias SignedNetworkParameters = SignedDataWithCert<NetworkParameters>
|
||||
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.
|
||||
/**
|
||||
* 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()
|
||||
|
||||
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())
|
||||
|
||||
|
@ -1,16 +1,11 @@
|
||||
package com.r3.corda.networkmanage.doorman
|
||||
|
||||
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory
|
||||
import com.r3.corda.networkmanage.common.persistence.*
|
||||
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.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.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.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_NETWORK_MAP_CERTIFICATE_NAME
|
||||
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.nodeSerializationEnv
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
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.SerializationFactoryImpl
|
||||
import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||
import java.io.Closeable
|
||||
import java.net.URI
|
||||
import java.nio.file.Path
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import kotlin.concurrent.thread
|
||||
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 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: ")
|
||||
|
||||
if (rootStore.containsAlias(X509Utilities.CORDA_ROOT_CA)) {
|
||||
val oldKey = loadOrCreateKeyStore(rootStoreFile, rootKeystorePassword).getCertificate(X509Utilities.CORDA_ROOT_CA).publicKey
|
||||
println("Key ${X509Utilities.CORDA_ROOT_CA} already exists in keystore, process will now terminate.")
|
||||
println(oldKey)
|
||||
println("${X509Utilities.CORDA_ROOT_CA} already exists in keystore, process will now terminate.")
|
||||
println(rootStore.getCertificate(X509Utilities.CORDA_ROOT_CA))
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
@ -203,7 +75,7 @@ fun generateRootKeyPair(rootStoreFile: Path, rootKeystorePass: String?, rootPriv
|
||||
println("Trust store for distribution to nodes created in $nodeTrustStore")
|
||||
|
||||
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?) {
|
||||
@ -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
|
||||
|
||||
// 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 keyStore = loadOrCreateKeyStore(parameters.keystorePath, keyStorePassword)
|
||||
|
||||
val (doormanSigner, networkMapSigner) = listOf(DEFAULT_CSR_CERTIFICATE_NAME, DEFAULT_NETWORK_MAP_CERTIFICATE_NAME).map {
|
||||
val keyPair = keyStore.getKeyPair(it, privateKeyPassword)
|
||||
val certPath = keyStore.getCertificateChain(it).map { it as X509Certificate }
|
||||
LocalSigner(keyPair, certPath.toTypedArray())
|
||||
val csrCertPathAndKey = keyStore.run {
|
||||
CertPathAndKey(
|
||||
keyStore.getCertificateChain(DEFAULT_CSR_CERTIFICATE_NAME).map { it as X509Certificate },
|
||||
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.
|
||||
*/
|
||||
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 {
|
||||
val requestId = delegate.saveRequest(request)
|
||||
delegate.markRequestTicketCreated(requestId)
|
||||
@ -311,9 +186,9 @@ fun main(args: Array<String>) {
|
||||
initialiseSerialization()
|
||||
val database = configureDatabase(dataSourceProperties)
|
||||
// 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")
|
||||
}
|
||||
|
||||
@ -325,10 +200,10 @@ fun main(args: Array<String>) {
|
||||
parseNetworkParametersFrom(it)
|
||||
}
|
||||
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) {
|
||||
networkManagementServer.close()
|
||||
|
@ -1,58 +1,142 @@
|
||||
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.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 net.corda.nodeapi.internal.network.NetworkParameters
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
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
|
||||
|
||||
/**
|
||||
* NetworkManagementWebServer runs on Jetty server and provides service via http.
|
||||
*/
|
||||
class NetworkManagementWebServer(hostAndPort: NetworkHostAndPort, private vararg val webServices: Any) : Closeable {
|
||||
class NetworkManagementServer : Closeable {
|
||||
companion object {
|
||||
val logger = loggerFor<NetworkManagementServer>()
|
||||
private val logger = loggerFor<NetworkManagementServer>()
|
||||
}
|
||||
|
||||
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()
|
||||
private val closeActions = mutableListOf<() -> Unit>()
|
||||
lateinit var hostAndPort: NetworkHostAndPort
|
||||
|
||||
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) }
|
||||
for (closeAction in closeActions) {
|
||||
try {
|
||||
closeAction()
|
||||
} catch (e: Exception) {
|
||||
logger.warn("Discregarding exception thrown during close", e)
|
||||
}
|
||||
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
|
||||
|
||||
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.Companion.DOORMAN_SIGNATURE
|
||||
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
||||
import com.r3.corda.networkmanage.doorman.JiraClient
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import com.r3.corda.networkmanage.common.utils.CertPathAndKey
|
||||
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.jcajce.JcaPKCS10CertificationRequest
|
||||
import java.security.cert.CertPath
|
||||
import javax.security.auth.x500.X500Principal
|
||||
|
||||
interface CsrHandler {
|
||||
fun saveRequest(rawRequest: PKCS10CertificationRequest): String
|
||||
@ -16,26 +23,21 @@ interface CsrHandler {
|
||||
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() {
|
||||
storage.getRequests(RequestStatus.APPROVED)
|
||||
.forEach { processRequest(it.requestId, it.request) }
|
||||
if (csrCertPathAndKey == null) return
|
||||
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() {}
|
||||
|
||||
private fun processRequest(requestId: String, request: PKCS10CertificationRequest) {
|
||||
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 saveRequest(rawRequest: PKCS10CertificationRequest): String = storage.saveRequest(rawRequest)
|
||||
|
||||
override fun getResponse(requestId: String): CertificateResponse {
|
||||
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."))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class JiraCsrHandler(private val jiraClient: JiraClient, private val storage: CertificationRequestStorage, private val delegate: CsrHandler) : CsrHandler by delegate {
|
||||
private companion object {
|
||||
val log = loggerFor<JiraCsrHandler>()
|
||||
}
|
||||
|
||||
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)
|
||||
private fun createSignedNodeCertificate(certificationRequest: PKCS10CertificationRequest,
|
||||
csrCertPathAndKey: CertPathAndKey): 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,
|
||||
csrCertPathAndKey.certPath[0],
|
||||
csrCertPathAndKey.toKeyPair(),
|
||||
X500Principal(request.subject.encoded),
|
||||
request.publicKey,
|
||||
nameConstraints = nameConstraints)
|
||||
return X509CertificateFactory().generateCertPath(listOf(nodeCaCert) + csrCertPathAndKey.certPath)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
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.internal.DigitalSignatureWithCert
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
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.jcajce.JcaPKCS10CertificationRequest
|
||||
import java.security.KeyPair
|
||||
import java.security.cert.CertPath
|
||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||
import java.security.KeyStore
|
||||
import java.security.PrivateKey
|
||||
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 is intended to be used in testing environment where hardware signing module is not available.
|
||||
* This local signer 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 signingKeyPair: KeyPair, private val signingCertPath: Array<X509Certificate>) : Signer {
|
||||
// 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)
|
||||
}
|
||||
class LocalSigner(private val signingKey: PrivateKey, private val signingCert: X509Certificate) : Signer {
|
||||
constructor(certAndKeyPair: CertificateAndKeyPair) : this(certAndKeyPair.keyPair.private, certAndKeyPair.certificate)
|
||||
|
||||
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.NodeInfoWithSigned
|
||||
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.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.node.NodeInfo
|
||||
import net.corda.core.serialization.deserialize
|
||||
@ -31,9 +30,9 @@ import javax.ws.rs.core.Response.ok
|
||||
import javax.ws.rs.core.Response.status
|
||||
|
||||
@Path(NETWORK_MAP_PATH)
|
||||
class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage,
|
||||
private val networkMapStorage: NetworkMapStorage,
|
||||
private val config: NetworkMapConfig) {
|
||||
class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage,
|
||||
private val networkMapStorage: NetworkMapStorage,
|
||||
private val config: NetworkMapConfig) {
|
||||
|
||||
companion object {
|
||||
val log = contextLogger()
|
||||
@ -42,7 +41,7 @@ class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage,
|
||||
|
||||
private val networkMapCache: LoadingCache<Boolean, Pair<SignedNetworkMap?, NetworkParameters?>> = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(config.cacheTimeout, TimeUnit.MILLISECONDS)
|
||||
.build(CacheLoader.from { _ -> Pair(networkMapStorage.getCurrentNetworkMap(), networkMapStorage.getCurrentSignedNetworkParameters()?.verified()) })
|
||||
.build(CacheLoader.from { _ -> Pair(networkMapStorage.getCurrentNetworkMap(), networkMapStorage.getNetworkParametersOfNetworkMap()?.verified()) })
|
||||
|
||||
@POST
|
||||
@Path("publish")
|
@ -1,7 +1,10 @@
|
||||
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.configureDatabase
|
||||
import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
|
||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
|
||||
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.HsmNetworkMapSigner
|
||||
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 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
|
||||
|
||||
private val log = LogManager.getLogger("com.r3.corda.networkmanage.hsm.Main")
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
// Grabbed from https://stackoverflow.com/questions/7953567/checking-if-unlimited-cryptography-is-available
|
||||
if (Cipher.getMaxAllowedKeyLength("AES") < 256) {
|
||||
@ -46,12 +58,15 @@ fun run(parameters: Parameters) {
|
||||
checkNotNull(dataSourceProperties)
|
||||
val database = configureDatabase(dataSourceProperties, databaseConfig)
|
||||
val csrStorage = DBSignedCertificateRequestStorage(database)
|
||||
val networkMapStorage = PersistentNetworkMapStorage(database)
|
||||
val hsmNetworkMapSigningThread = HsmNetworkMapSigner(
|
||||
networkMapStorage,
|
||||
val hsmSigner = HsmNetworkMapSigner(
|
||||
networkMapCertificateName,
|
||||
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 signer = HsmCsrSigner(
|
||||
csrStorage,
|
||||
@ -62,6 +77,7 @@ fun run(parameters: Parameters) {
|
||||
Authenticator(createProvider(), authMode, autoUsername, authKeyFilePath, authKeyFilePassword, signAuthThreshold))
|
||||
signer.sign(it)
|
||||
}
|
||||
|
||||
Menu().withExceptionHandler(::processError).addItem("1", "Generate root and intermediate certificates", {
|
||||
if (confirmedKeyGen()) {
|
||||
val generator = KeyCertificateGenerator(
|
||||
@ -104,10 +120,25 @@ fun run(parameters: Parameters) {
|
||||
println("There is no approved and unsigned CSR")
|
||||
}
|
||||
}).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) {
|
||||
val processed = mapCryptoServerException(exception)
|
||||
System.err.println("An error occurred: ${processed.message}")
|
||||
|
@ -1,78 +1,39 @@
|
||||
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.hsm.authentication.Authenticator
|
||||
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.verify
|
||||
import net.corda.core.internal.DigitalSignatureWithCert
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.minutes
|
||||
import net.corda.nodeapi.internal.crypto.getX509Certificate
|
||||
import java.security.PrivateKey
|
||||
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.
|
||||
* It uses HSM as the signing entity with keys and certificates specified at the construction time.
|
||||
* Signer which connects to a HSM using the given [authenticator] to sign bytes.
|
||||
*/
|
||||
// TODO Rename this to HsmSigner
|
||||
class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage,
|
||||
private val caCertificateKeyName: String,
|
||||
private val caPrivateKeyPass: String,
|
||||
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)
|
||||
}
|
||||
|
||||
class HsmNetworkMapSigner(private val certificateKeyName: String,
|
||||
private val privateKeyPassword: String,
|
||||
private val authenticator: Authenticator) : Signer {
|
||||
/**
|
||||
* Signs given data using [CryptoServerJCE.CryptoServerProvider], which connects to the underlying HSM.
|
||||
*/
|
||||
override fun signBytes(data: ByteArray): DigitalSignatureWithCert {
|
||||
return authenticator.connectAndAuthenticate { provider, _ ->
|
||||
val keyStore = getAndInitializeKeyStore(provider)
|
||||
val caCertificateChain = keyStore.getCertificateChain(caCertificateKeyName)
|
||||
val caKey = keyStore.getKey(caCertificateKeyName, caPrivateKeyPass.toCharArray()) as PrivateKey
|
||||
val certificate = keyStore.getX509Certificate(certificateKeyName)
|
||||
// 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 {
|
||||
initSign(caKey)
|
||||
initSign(key)
|
||||
update(data)
|
||||
sign()
|
||||
}
|
||||
verify(data, signature, caCertificateChain[0].publicKey)
|
||||
DigitalSignatureWithCert(caCertificateChain[0] as X509Certificate, signature)
|
||||
verify(data, signature, certificate.publicKey)
|
||||
DigitalSignatureWithCert(certificate, signature)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
||||
|
||||
// then
|
||||
val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
||||
val persistedSignedParameters = networkMapStorage.getCurrentSignedNetworkParameters()
|
||||
val persistedSignedParameters = networkMapStorage.getNetworkParametersOfNetworkMap()
|
||||
|
||||
assertEquals(networkParameters, persistedSignedParameters?.verifiedNetworkMapCert(rootCaCert))
|
||||
assertEquals(parametersSignature, persistedSignedParameters?.sig)
|
||||
@ -84,13 +84,13 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
||||
networkMapStorage.saveNetworkParameters(params2, null)
|
||||
|
||||
// when
|
||||
val latest = networkMapStorage.getLatestUnsignedNetworkParameters()
|
||||
val latest = networkMapStorage.getLatestNetworkParameters()?.minimumPlatformVersion
|
||||
// then
|
||||
assertEquals(2, latest.minimumPlatformVersion)
|
||||
assertEquals(2, latest)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getCurrentNetworkParameters returns current network map parameters`() {
|
||||
fun `getNetworkParametersOfNetworkMap returns current network map parameters`() {
|
||||
// given
|
||||
// Create network parameters
|
||||
val testParameters1 = testNetworkParameters(emptyList())
|
||||
@ -107,7 +107,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
||||
networkMapStorage.saveNetworkParameters(testParameters2, testParameters2.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate).sig)
|
||||
|
||||
// when
|
||||
val result = networkMapStorage.getCurrentSignedNetworkParameters()?.verifiedNetworkMapCert(rootCaCert)
|
||||
val result = networkMapStorage.getNetworkParametersOfNetworkMap()?.verifiedNetworkMapCert(rootCaCert)
|
||||
|
||||
// then
|
||||
assertEquals(1, result?.minimumPlatformVersion)
|
||||
|
@ -51,8 +51,8 @@ class NetworkMapSignerTest : TestBase() {
|
||||
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
|
||||
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
||||
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes)
|
||||
whenever(networkMapStorage.getLatestUnsignedNetworkParameters()).thenReturn(latestNetworkParameters)
|
||||
whenever(networkMapStorage.getCurrentSignedNetworkParameters()).thenReturn(currentParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(latestNetworkParameters)
|
||||
whenever(networkMapStorage.getNetworkParametersOfNetworkMap()).thenReturn(currentParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
||||
whenever(signer.signBytes(any())).then {
|
||||
DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray))
|
||||
}
|
||||
@ -67,8 +67,8 @@ class NetworkMapSignerTest : TestBase() {
|
||||
// then
|
||||
// Verify networkMapStorage calls
|
||||
verify(networkMapStorage).getNodeInfoHashes(any())
|
||||
verify(networkMapStorage).getLatestUnsignedNetworkParameters()
|
||||
verify(networkMapStorage).getCurrentSignedNetworkParameters()
|
||||
verify(networkMapStorage).getLatestNetworkParameters()
|
||||
verify(networkMapStorage).getNetworkParametersOfNetworkMap()
|
||||
argumentCaptor<SignedNetworkMap>().apply {
|
||||
verify(networkMapStorage).saveNetworkMap(capture())
|
||||
val capturedNetworkMap = firstValue.verifiedNetworkMapCert(rootCaCert)
|
||||
@ -87,8 +87,8 @@ class NetworkMapSignerTest : TestBase() {
|
||||
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
|
||||
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
||||
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
|
||||
whenever(networkMapStorage.getLatestUnsignedNetworkParameters()).thenReturn(networkParameters)
|
||||
whenever(networkMapStorage.getCurrentSignedNetworkParameters()).thenReturn(networkParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
||||
whenever(networkMapStorage.getNetworkParametersOfNetworkMap()).thenReturn(networkParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
||||
|
||||
// when
|
||||
networkMapSigner.signNetworkMap()
|
||||
@ -104,7 +104,7 @@ class NetworkMapSignerTest : TestBase() {
|
||||
val networkParameters = testNetworkParameters(emptyList())
|
||||
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(null)
|
||||
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
|
||||
whenever(networkMapStorage.getLatestUnsignedNetworkParameters()).thenReturn(networkParameters)
|
||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
||||
whenever(signer.signBytes(any())).then {
|
||||
DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray))
|
||||
}
|
||||
@ -118,7 +118,7 @@ class NetworkMapSignerTest : TestBase() {
|
||||
// then
|
||||
// Verify networkMapStorage calls
|
||||
verify(networkMapStorage).getNodeInfoHashes(any())
|
||||
verify(networkMapStorage).getLatestUnsignedNetworkParameters()
|
||||
verify(networkMapStorage).getLatestNetworkParameters()
|
||||
argumentCaptor<SignedNetworkMap>().apply {
|
||||
verify(networkMapStorage).saveNetworkMap(capture())
|
||||
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.mockito.Mock
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
import java.security.cert.CertPath
|
||||
|
||||
class JiraCsrHandlerTest {
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val mockitoRule = MockitoJUnit.rule()
|
||||
val mockitoRule: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
@Mock
|
||||
lateinit var jiraClient: JiraClient
|
||||
private lateinit var jiraClient: JiraClient
|
||||
|
||||
@Mock
|
||||
lateinit var certificationRequestStorage: CertificationRequestStorage
|
||||
private lateinit var certificationRequestStorage: CertificationRequestStorage
|
||||
|
||||
@Mock
|
||||
lateinit var defaultCsrHandler: DefaultCsrHandler
|
||||
private lateinit var defaultCsrHandler: DefaultCsrHandler
|
||||
|
||||
@Mock
|
||||
var certPath: CertPath = mock()
|
||||
private val certPath: CertPath = mock()
|
||||
|
||||
private lateinit var jiraCsrHandler: JiraCsrHandler
|
||||
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.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.utils.SignedNetworkMap
|
||||
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.identity.CordaX500Name
|
||||
import net.corda.core.internal.checkOkResponse
|
||||
@ -36,7 +37,7 @@ import java.security.cert.X509Certificate
|
||||
import javax.ws.rs.core.MediaType
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class NodeInfoWebServiceTest {
|
||||
class NetworkMapWebServiceTest {
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule(true)
|
||||
@ -56,12 +57,12 @@ class NodeInfoWebServiceTest {
|
||||
@Test
|
||||
fun `submit nodeInfo`() {
|
||||
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.
|
||||
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()
|
||||
val nodeInfoAndSignature = signedNodeInfo.serialize().bytes
|
||||
// Post node info and signature to doorman, this should pass without any exception.
|
||||
@ -72,12 +73,12 @@ class NodeInfoWebServiceTest {
|
||||
@Test
|
||||
fun `submit old nodeInfo`() {
|
||||
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.
|
||||
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()
|
||||
val nodeInfoAndSignature = signedNodeInfo.serialize().bytes
|
||||
assertThatThrownBy { it.doPost("publish", nodeInfoAndSignature) }
|
||||
@ -88,12 +89,12 @@ class NodeInfoWebServiceTest {
|
||||
@Test
|
||||
fun `submit nodeInfo when no network parameters`() {
|
||||
val networkMapStorage: NetworkMapStorage = mock {
|
||||
on { getCurrentSignedNetworkParameters() }.thenReturn(null)
|
||||
on { getNetworkParametersOfNetworkMap() }.thenReturn(null)
|
||||
}
|
||||
// Create node info.
|
||||
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()
|
||||
val nodeInfoAndSignature = signedNodeInfo.serialize().bytes
|
||||
assertThatThrownBy { it.doPost("publish", nodeInfoAndSignature) }
|
||||
@ -110,7 +111,7 @@ class NodeInfoWebServiceTest {
|
||||
on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap)
|
||||
}
|
||||
|
||||
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(mock(), networkMapStorage, testNetworkMapConfig)).use {
|
||||
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NetworkMapWebService(mock(), networkMapStorage, testNetworkMapConfig)).use {
|
||||
it.start()
|
||||
val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("")
|
||||
verify(networkMapStorage, times(1)).getCurrentNetworkMap()
|
||||
@ -127,7 +128,7 @@ class NodeInfoWebServiceTest {
|
||||
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()
|
||||
val nodeInfoResponse = it.doGet<SignedNodeInfo>("node-info/$nodeInfoHash")
|
||||
verify(nodeInfoStorage, times(1)).getNodeInfo(nodeInfoHash)
|
||||
@ -149,7 +150,7 @@ class NodeInfoWebServiceTest {
|
||||
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()
|
||||
val netParamsResponse = it.doGet<SignedNetworkParameters>("network-parameters/$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.r3.corda.networkmanage.TestBase
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
||||
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.webservice.RegistrationWebService
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.CordaX500Name
|
Loading…
Reference in New Issue
Block a user