mirror of
https://github.com/corda/corda.git
synced 2024-12-28 00:38:55 +00:00
Removing NodeInfo signing process - this task is moved to node itself (#112)
* Removing NodeInfo signing process - this task is moved to node itself * Addressing review comments * Introducing HSM simulator polling * Addressing review comments
This commit is contained in:
parent
7786913cd8
commit
c1ae6e9647
@ -1,7 +1,7 @@
|
|||||||
ext {
|
ext {
|
||||||
// We use Corda release artifact dependencies instead of project dependencies to make sure each doorman releases are
|
// We use Corda release artifact dependencies instead of project dependencies to make sure each doorman releases are
|
||||||
// aligned with the corresponding Corda release.
|
// aligned with the corresponding Corda release.
|
||||||
corda_dependency_version = '2.0-20171108.000038-27'
|
corda_dependency_version = '3.0-20171115.000100-7'
|
||||||
}
|
}
|
||||||
|
|
||||||
version "$corda_dependency_version"
|
version "$corda_dependency_version"
|
||||||
@ -125,7 +125,6 @@ dependencies {
|
|||||||
testCompile "com.nhaarman:mockito-kotlin:0.6.1"
|
testCompile "com.nhaarman:mockito-kotlin:0.6.1"
|
||||||
testRuntime "net.corda:corda-rpc:$corda_dependency_version"
|
testRuntime "net.corda:corda-rpc:$corda_dependency_version"
|
||||||
testCompile "com.spotify:docker-client:8.9.1"
|
testCompile "com.spotify:docker-client:8.9.1"
|
||||||
integrationTestCompile "net.corda:corda-test-common:$corda_dependency_version"
|
|
||||||
integrationTestRuntime "net.corda:corda-rpc:$corda_dependency_version"
|
integrationTestRuntime "net.corda:corda-rpc:$corda_dependency_version"
|
||||||
|
|
||||||
compile('com.atlassian.jira:jira-rest-java-client-core:4.0.0') {
|
compile('com.atlassian.jira:jira-rest-java-client-core:4.0.0') {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package com.r3.corda.networkmanage
|
package com.r3.corda.networkmanage
|
||||||
|
|
||||||
|
import CryptoServerAPI.CryptoServerException
|
||||||
|
import com.r3.corda.networkmanage.hsm.authentication.CryptoServerProviderConfig
|
||||||
|
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
||||||
import com.spotify.docker.client.DefaultDockerClient
|
import com.spotify.docker.client.DefaultDockerClient
|
||||||
import com.spotify.docker.client.DockerClient
|
import com.spotify.docker.client.DockerClient
|
||||||
import com.spotify.docker.client.messages.ContainerConfig
|
import com.spotify.docker.client.messages.ContainerConfig
|
||||||
@ -29,7 +32,9 @@ data class CryptoUserCredentials(val username: String, val password: String)
|
|||||||
*/
|
*/
|
||||||
class HsmSimulator(private val serverAddress: String = DEFAULT_SERVER_ADDRESS,
|
class HsmSimulator(private val serverAddress: String = DEFAULT_SERVER_ADDRESS,
|
||||||
private val imageRepoTag: String = DEFAULT_IMAGE_REPO_TAG,
|
private val imageRepoTag: String = DEFAULT_IMAGE_REPO_TAG,
|
||||||
private val imageVersion: String = DEFAULT_IMAGE_VERSION) : ExternalResource() {
|
private val imageVersion: String = DEFAULT_IMAGE_VERSION,
|
||||||
|
private val registryUser: String? = REGISTRY_USERNAME,
|
||||||
|
private val registryPass: String? = REGISTRY_PASSWORD) : ExternalResource() {
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
val DEFAULT_SERVER_ADDRESS = "corda.azurecr.io"
|
val DEFAULT_SERVER_ADDRESS = "corda.azurecr.io"
|
||||||
@ -46,6 +51,9 @@ class HsmSimulator(private val serverAddress: String = DEFAULT_SERVER_ADDRESS,
|
|||||||
val REGISTRY_PASSWORD = System.getenv("AZURE_CR_PASS")
|
val REGISTRY_PASSWORD = System.getenv("AZURE_CR_PASS")
|
||||||
|
|
||||||
val log = loggerFor<HsmSimulator>()
|
val log = loggerFor<HsmSimulator>()
|
||||||
|
|
||||||
|
private val HSM_STARTUP_SLEEP_INTERVAL_MS = 500L
|
||||||
|
private val HSM_STARTUP_POLL_MAX_COUNT = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
private val localHostAndPortBinding = freeLocalHostAndPort()
|
private val localHostAndPortBinding = freeLocalHostAndPort()
|
||||||
@ -53,8 +61,8 @@ class HsmSimulator(private val serverAddress: String = DEFAULT_SERVER_ADDRESS,
|
|||||||
private var containerId: String? = null
|
private var containerId: String? = null
|
||||||
|
|
||||||
override fun before() {
|
override fun before() {
|
||||||
assumeFalse("Docker registry username is not set!. Skipping the test.", REGISTRY_USERNAME.isNullOrBlank())
|
assumeFalse("Docker registry username is not set!. Skipping the test.", registryUser.isNullOrBlank())
|
||||||
assumeFalse("Docker registry password is not set!. Skipping the test.", REGISTRY_PASSWORD.isNullOrBlank())
|
assumeFalse("Docker registry password is not set!. Skipping the test.", registryPass.isNullOrBlank())
|
||||||
docker = DefaultDockerClient.fromEnv().build().pullHsmSimulatorImageFromRepository()
|
docker = DefaultDockerClient.fromEnv().build().pullHsmSimulatorImageFromRepository()
|
||||||
containerId = docker.createContainer()
|
containerId = docker.createContainer()
|
||||||
docker.startHsmSimulatorContainer()
|
docker.startHsmSimulatorContainer()
|
||||||
@ -96,17 +104,41 @@ class HsmSimulator(private val serverAddress: String = DEFAULT_SERVER_ADDRESS,
|
|||||||
if (containerId != null) {
|
if (containerId != null) {
|
||||||
log.debug("Starting container $containerId...")
|
log.debug("Starting container $containerId...")
|
||||||
this.startContainer(containerId)
|
this.startContainer(containerId)
|
||||||
|
pollAndWaitForHsmSimulator()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun pollAndWaitForHsmSimulator() {
|
||||||
|
val config = CryptoServerProviderConfig(
|
||||||
|
Device = "${localHostAndPortBinding.port}@${localHostAndPortBinding.host}",
|
||||||
|
KeyGroup = "*",
|
||||||
|
KeySpecifier = -1
|
||||||
|
)
|
||||||
|
var pollCount = HSM_STARTUP_POLL_MAX_COUNT
|
||||||
|
while (pollCount > 0) {
|
||||||
|
val provider = createProvider(config)
|
||||||
|
try {
|
||||||
|
provider.loginPassword(CRYPTO_USER, CRYPTO_PASSWORD)
|
||||||
|
provider.cryptoServer.authState
|
||||||
|
return
|
||||||
|
} catch (e: CryptoServerException) {
|
||||||
|
pollCount--
|
||||||
|
Thread.sleep(HSM_STARTUP_SLEEP_INTERVAL_MS)
|
||||||
|
} finally {
|
||||||
|
provider.logoff()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw IllegalStateException("Unable to obtain connection to initialised HSM Simulator")
|
||||||
|
}
|
||||||
|
|
||||||
private fun getImageFullName() = "$imageRepoTag:$imageVersion"
|
private fun getImageFullName() = "$imageRepoTag:$imageVersion"
|
||||||
|
|
||||||
private fun DockerClient.pullHsmSimulatorImageFromRepository(): DockerClient {
|
private fun DockerClient.pullHsmSimulatorImageFromRepository(): DockerClient {
|
||||||
this.pull(imageRepoTag,
|
this.pull(imageRepoTag,
|
||||||
RegistryAuth.builder()
|
RegistryAuth.builder()
|
||||||
.serverAddress(serverAddress)
|
.serverAddress(serverAddress)
|
||||||
.username(REGISTRY_USERNAME)
|
.username(registryUser)
|
||||||
.password(REGISTRY_PASSWORD)
|
.password(registryPass)
|
||||||
.build())
|
.build())
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -2,63 +2,73 @@ package com.r3.corda.networkmanage.doorman
|
|||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import com.nhaarman.mockito_kotlin.whenever
|
||||||
import com.r3.corda.networkmanage.common.persistence.SchemaService
|
import com.r3.corda.networkmanage.common.persistence.SchemaService
|
||||||
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||||
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.crypto.SignedData
|
||||||
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.cert
|
import net.corda.core.internal.cert
|
||||||
|
import net.corda.core.node.NodeInfo
|
||||||
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
|
import net.corda.node.services.network.NetworkMapClient
|
||||||
import net.corda.node.utilities.*
|
import net.corda.node.utilities.*
|
||||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||||
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.testNodeConfiguration
|
import net.corda.testing.testNodeConfiguration
|
||||||
|
import org.bouncycastle.cert.X509CertificateHolder
|
||||||
|
import org.junit.Ignore
|
||||||
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.net.URL
|
import java.net.URL
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
class DoormanIntegrationTest {
|
class DoormanIntegrationTest {
|
||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val tempFolder = TemporaryFolder()
|
val tempFolder = TemporaryFolder()
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val testSerialization = SerializationEnvironmentRule(true)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial registration`() {
|
fun `initial registration`() {
|
||||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val rootCertAndKey = createDoormanRootCertificateAndKeyPair()
|
||||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Integration Test Corda Node Root CA", organisation = "R3 Ltd", locality = "London", country = "GB"), rootCAKey)
|
val intermediateCertAndKey = createDoormanIntermediateCertificateAndKeyPair(rootCertAndKey)
|
||||||
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey,
|
|
||||||
CordaX500Name(commonName = "Integration Test Corda Node Intermediate CA", locality = "London", country = "GB", organisation = "R3 Ltd"), intermediateCAKey.public)
|
|
||||||
|
|
||||||
val database = configureDatabase(makeTestDataSourceProperties(), null, {
|
|
||||||
// Identity service not needed doorman, corda persistence is not very generic.
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
}, SchemaService())
|
|
||||||
val signer = LocalSigner(intermediateCAKey, arrayOf(intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()))
|
|
||||||
|
|
||||||
//Start doorman server
|
//Start doorman server
|
||||||
val doorman = startDoorman(NetworkHostAndPort("localhost", 0), database, true, testNetworkParameters(emptyList()), signer, 2, 10,null)
|
val doorman = startDoorman(intermediateCertAndKey, rootCertAndKey.certificate)
|
||||||
|
|
||||||
// Start Corda network registration.
|
// Start Corda network registration.
|
||||||
val config = testNodeConfiguration(
|
val config = testNodeConfiguration(
|
||||||
baseDirectory = tempFolder.root.toPath(),
|
baseDirectory = tempFolder.root.toPath(),
|
||||||
myLegalName = ALICE.name).also {
|
myLegalName = ALICE.name).also {
|
||||||
whenever(it.certificateSigningService).thenReturn(URL("http://localhost:${doorman.hostAndPort.port}"))
|
val doormanHostAndPort = doorman.hostAndPort
|
||||||
|
whenever(it.compatibilityZoneURL).thenReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}"))
|
||||||
whenever(it.emailAddress).thenReturn("iTest@R3.com")
|
whenever(it.emailAddress).thenReturn("iTest@R3.com")
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.certificateSigningService)).buildKeystore()
|
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
||||||
|
|
||||||
// Checks the keystore are created with the right certificates and keys.
|
// Checks the keystore are created with the right certificates and keys.
|
||||||
assert(config.nodeKeystore.toFile().exists())
|
assert(config.nodeKeystore.toFile().exists())
|
||||||
assert(config.sslKeystore.toFile().exists())
|
assert(config.sslKeystore.toFile().exists())
|
||||||
assert(config.trustStoreFile.toFile().exists())
|
assert(config.trustStoreFile.toFile().exists())
|
||||||
|
|
||||||
|
val intermediateCACert = intermediateCertAndKey.certificate
|
||||||
|
val rootCACert = rootCertAndKey.certificate
|
||||||
|
|
||||||
loadKeyStore(config.nodeKeystore, config.keyStorePassword).apply {
|
loadKeyStore(config.nodeKeystore, config.keyStorePassword).apply {
|
||||||
assert(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
assert(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
||||||
assertEquals(ALICE.name.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN).x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_CA).subjectX500Principal)
|
assertEquals(ALICE.name.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN).x500Principal, getX509Certificate(X509Utilities.CORDA_CLIENT_CA).subjectX500Principal)
|
||||||
@ -75,15 +85,91 @@ class DoormanIntegrationTest {
|
|||||||
assert(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
assert(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||||
assertEquals(rootCACert.cert.subjectX500Principal, getX509Certificate(X509Utilities.CORDA_ROOT_CA).subjectX500Principal)
|
assertEquals(rootCACert.cert.subjectX500Principal, getX509Certificate(X509Utilities.CORDA_ROOT_CA).subjectX500Principal)
|
||||||
}
|
}
|
||||||
|
|
||||||
doorman.close()
|
doorman.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties {
|
//TODO remove @Ignore once PR https://github.com/corda/corda/pull/2054 is merged
|
||||||
val props = Properties()
|
@Test
|
||||||
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
|
@Ignore
|
||||||
props.setProperty("dataSource.url", "jdbc:h2:mem:${nodeName}_persistence;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE")
|
fun `nodeInfo is published to the network map`() {
|
||||||
props.setProperty("dataSource.user", "sa")
|
// Given
|
||||||
props.setProperty("dataSource.password", "")
|
val rootCertAndKey = createDoormanRootCertificateAndKeyPair()
|
||||||
return props
|
val intermediateCertAndKey = createDoormanIntermediateCertificateAndKeyPair(rootCertAndKey)
|
||||||
|
|
||||||
|
//Start doorman server
|
||||||
|
val doorman = startDoorman(intermediateCertAndKey, rootCertAndKey.certificate)
|
||||||
|
val doormanHostAndPort = doorman.hostAndPort
|
||||||
|
|
||||||
|
// Start Corda network registration.
|
||||||
|
val config = testNodeConfiguration(
|
||||||
|
baseDirectory = tempFolder.root.toPath(),
|
||||||
|
myLegalName = ALICE.name).also {
|
||||||
|
whenever(it.compatibilityZoneURL).thenReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}"))
|
||||||
|
whenever(it.emailAddress).thenReturn("iTest@R3.com")
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
||||||
|
|
||||||
|
// Publish NodeInfo
|
||||||
|
val networkMapClient = NetworkMapClient(config.compatibilityZoneURL!!)
|
||||||
|
val certs = loadKeyStore(config.nodeKeystore, config.keyStorePassword).getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||||
|
val keyPair = loadKeyStore(config.nodeKeystore, config.keyStorePassword).getKeyPair(X509Utilities.CORDA_CLIENT_CA, config.keyStorePassword)
|
||||||
|
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(buildCertPath(*certs))), 1, serial = 1L)
|
||||||
|
val nodeInfoBytes = nodeInfo.serialize()
|
||||||
|
|
||||||
|
// When
|
||||||
|
networkMapClient.publish(SignedData(nodeInfoBytes, keyPair.sign(nodeInfoBytes)))
|
||||||
|
|
||||||
|
// Then
|
||||||
|
val networkMapNodeInfo = networkMapClient.getNodeInfo(nodeInfoBytes.hash)
|
||||||
|
assertNotNull(networkMapNodeInfo)
|
||||||
|
assertEquals(nodeInfo, networkMapNodeInfo)
|
||||||
|
|
||||||
|
doorman.close()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createDoormanIntermediateCertificateAndKeyPair(rootCertificateAndKeyPair: CertificateAndKeyPair): CertificateAndKeyPair {
|
||||||
|
val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCertificateAndKeyPair.certificate, rootCertificateAndKeyPair.keyPair,
|
||||||
|
CordaX500Name(commonName = "Integration Test Corda Node Intermediate CA",
|
||||||
|
locality = "London",
|
||||||
|
country = "GB",
|
||||||
|
organisation = "R3 Ltd"), intermediateCAKey.public)
|
||||||
|
return CertificateAndKeyPair(intermediateCACert, intermediateCAKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createDoormanRootCertificateAndKeyPair(): CertificateAndKeyPair {
|
||||||
|
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
|
val rootCACert = X509Utilities.createSelfSignedCACertificate(
|
||||||
|
CordaX500Name(commonName = "Integration Test Corda Node Root CA",
|
||||||
|
organisation = "R3 Ltd", locality = "London",
|
||||||
|
country = "GB"), rootCAKey)
|
||||||
|
return CertificateAndKeyPair(rootCACert, rootCAKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties {
|
||||||
|
val props = Properties()
|
||||||
|
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
|
||||||
|
props.setProperty("dataSource.url", "jdbc:h2:mem:${nodeName}_persistence;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE")
|
||||||
|
props.setProperty("dataSource.user", "sa")
|
||||||
|
props.setProperty("dataSource.password", "")
|
||||||
|
return props
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startDoorman(intermediateCACertAndKey: CertificateAndKeyPair, rootCACert: X509CertificateHolder): DoormanServer {
|
||||||
|
val signer = LocalSigner(intermediateCACertAndKey.keyPair,
|
||||||
|
arrayOf(intermediateCACertAndKey.certificate.toX509Certificate(), rootCACert.toX509Certificate()))
|
||||||
|
//Start doorman server
|
||||||
|
return startDoorman(signer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startDoorman(localSigner: LocalSigner? = null): DoormanServer {
|
||||||
|
val database = configureDatabase(makeTestDataSourceProperties(), null, {
|
||||||
|
// Identity service not needed doorman, corda persistence is not very generic.
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}, SchemaService())
|
||||||
|
//Start doorman server
|
||||||
|
return startDoorman(NetworkHostAndPort("localhost", 0), database, true, testNetworkParameters(emptyList()), localSigner, 2, 30,null)
|
||||||
}
|
}
|
@ -11,6 +11,7 @@ import com.r3.corda.networkmanage.hsm.configuration.Parameters
|
|||||||
import org.junit.Before
|
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 kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class HsmTest {
|
class HsmTest {
|
||||||
@ -18,26 +19,30 @@ class HsmTest {
|
|||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val hsmSimulator: HsmSimulator = HsmSimulator()
|
val hsmSimulator: HsmSimulator = HsmSimulator()
|
||||||
|
val testParameters = Parameters(
|
||||||
|
dataSourceProperties = mock(),
|
||||||
|
device = "${hsmSimulator.port}@${hsmSimulator.host}",
|
||||||
|
keySpecifier = 1,
|
||||||
|
keyGroup = "*"
|
||||||
|
)
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val tempFolder = TemporaryFolder()
|
||||||
|
|
||||||
private lateinit var inputReader: InputReader
|
private lateinit var inputReader: InputReader
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
inputReader = mock()
|
inputReader = mock()
|
||||||
|
whenever(inputReader.readLine()).thenReturn(hsmSimulator.cryptoUserCredentials().username)
|
||||||
|
whenever(inputReader.readPassword(any())).thenReturn(hsmSimulator.cryptoUserCredentials().password)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Authenticator executes the block once user is successfully authenticated`() {
|
fun `Authenticator executes the block once user is successfully authenticated`() {
|
||||||
// given
|
// given
|
||||||
val parameters = Parameters(
|
val authenticator = Authenticator(testParameters.createProvider(), inputReader = inputReader)
|
||||||
dataSourceProperties = mock(),
|
|
||||||
device = "${hsmSimulator.port}@${hsmSimulator.host}",
|
|
||||||
keySpecifier = 1,
|
|
||||||
keyGroup = "*"
|
|
||||||
)
|
|
||||||
whenever(inputReader.readLine()).thenReturn(hsmSimulator.cryptoUserCredentials().username)
|
|
||||||
whenever(inputReader.readPassword(any())).thenReturn(hsmSimulator.cryptoUserCredentials().password)
|
|
||||||
val authenticator = Authenticator(parameters.createProvider(), inputReader = inputReader)
|
|
||||||
var executed = false
|
var executed = false
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -48,6 +53,4 @@ class HsmTest {
|
|||||||
// then
|
// then
|
||||||
assertTrue(executed)
|
assertTrue(executed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -86,19 +86,20 @@ class SigningServiceIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Signing service communicates with Doorman`() {
|
fun `Signing service signs approved CSRs`() {
|
||||||
//Start doorman server
|
//Start doorman server
|
||||||
val database = configureDatabase(makeTestDataSourceProperties(), null, {
|
val database = configureDatabase(makeTestDataSourceProperties(), null, {
|
||||||
// Identity service not needed doorman, corda persistence is not very generic.
|
// Identity service not needed doorman, corda persistence is not very generic.
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}, SchemaService())
|
}, SchemaService())
|
||||||
val doorman = startDoorman(NetworkHostAndPort(HOST, 0), database, approveAll = true, approveInterval = 2, signInterval = 10, initialNetworkMapParameters = testNetworkParameters(emptyList()))
|
val doorman = startDoorman(NetworkHostAndPort(HOST, 0), database, approveAll = true, approveInterval = 2, signInterval = 30, initialNetworkMapParameters = testNetworkParameters(emptyList()))
|
||||||
|
|
||||||
// Start Corda network registration.
|
// Start Corda network registration.
|
||||||
val config = testNodeConfiguration(
|
val config = testNodeConfiguration(
|
||||||
baseDirectory = tempFolder.root.toPath(),
|
baseDirectory = tempFolder.root.toPath(),
|
||||||
myLegalName = ALICE.name).also {
|
myLegalName = ALICE.name).also {
|
||||||
whenever(it.certificateSigningService).thenReturn(URL("http://$HOST:${doorman.hostAndPort.port}"))
|
val doormanHostAndPort = doorman.hostAndPort
|
||||||
|
whenever(it.compatibilityZoneURL).thenReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
val signingServiceStorage = DBSignedCertificateRequestStorage(configureDatabase(makeTestDataSourceProperties(), makeNotInitialisingTestDatabaseProperties(), {
|
val signingServiceStorage = DBSignedCertificateRequestStorage(configureDatabase(makeTestDataSourceProperties(), makeNotInitialisingTestDatabaseProperties(), {
|
||||||
@ -126,7 +127,7 @@ class SigningServiceIntegrationTest {
|
|||||||
// [org.hibernate.tool.schema.spi.SchemaManagementException] being thrown as the schema is missing.
|
// [org.hibernate.tool.schema.spi.SchemaManagementException] being thrown as the schema is missing.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.certificateSigningService)).buildKeystore()
|
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
||||||
verify(hsmSigner).sign(any())
|
verify(hsmSigner).sign(any())
|
||||||
doorman.close()
|
doorman.close()
|
||||||
}
|
}
|
||||||
@ -166,9 +167,9 @@ class SigningServiceIntegrationTest {
|
|||||||
3 -> CHARLIE.name
|
3 -> CHARLIE.name
|
||||||
else -> throw IllegalArgumentException("Unrecognised option")
|
else -> throw IllegalArgumentException("Unrecognised option")
|
||||||
}).also {
|
}).also {
|
||||||
whenever(it.certificateSigningService).thenReturn(URL("http://$HOST:${doorman.hostAndPort.port}"))
|
whenever(it.compatibilityZoneURL).thenReturn(URL("http://$HOST:${doorman.hostAndPort.port}"))
|
||||||
}
|
}
|
||||||
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.certificateSigningService)).buildKeystore()
|
NetworkRegistrationHelper(config, HTTPNetworkRegistrationService(config.compatibilityZoneURL!!)).buildKeystore()
|
||||||
}
|
}
|
||||||
}.map { it.join() }
|
}.map { it.join() }
|
||||||
doorman.close()
|
doorman.close()
|
||||||
|
@ -33,10 +33,10 @@ interface NetworkMapStorage {
|
|||||||
fun saveNetworkMap(signedNetworkMap: SignedNetworkMap)
|
fun saveNetworkMap(signedNetworkMap: SignedNetworkMap)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve all node info hashes for all signed node info with valid certificates,
|
* Retrieve all node info hashes for all node info with valid certificates,
|
||||||
* that are not associated with any network map yet.
|
* that are not associated with any network map yet.
|
||||||
*/
|
*/
|
||||||
fun getDetachedSignedAndValidNodeInfoHashes(): List<SecureHash>
|
fun getDetachedAndValidNodeInfoHashes(): List<SecureHash>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve network parameters by their hash.
|
* Retrieve network parameters by their hash.
|
||||||
|
@ -16,41 +16,16 @@ interface NodeInfoStorage {
|
|||||||
*/
|
*/
|
||||||
fun getCertificatePath(publicKeyHash: SecureHash): CertPath?
|
fun getCertificatePath(publicKeyHash: SecureHash): CertPath?
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain list of registered node info hashes that haven't been signed yet and have valid certificates.
|
|
||||||
*/
|
|
||||||
fun getUnsignedNodeInfoHashes(): List<SecureHash>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Similar to [getUnsignedNodeInfoHashes] but instead of hashes, map of node info bytes is returned.
|
|
||||||
* @return map of node info hashes to their corresponding node info bytes
|
|
||||||
*/
|
|
||||||
fun getUnsignedNodeInfoBytes(): Map<SecureHash, ByteArray>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve node info using nodeInfo's hash
|
|
||||||
* @return [NodeInfo] or null if the node info is not registered.
|
|
||||||
*/
|
|
||||||
fun getNodeInfo(nodeInfoHash: SecureHash): NodeInfo?
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve node info together with its signature using nodeInfo's hash
|
* Retrieve node info together with its signature using nodeInfo's hash
|
||||||
* @return [NodeInfo] or null if the node info is not registered.
|
* @return [NodeInfo] or null if the node info is not registered.
|
||||||
*/
|
*/
|
||||||
fun getSignedNodeInfo(nodeInfoHash: SecureHash): SignedData<NodeInfo>?
|
fun getNodeInfo(nodeInfoHash: SecureHash): SignedData<NodeInfo>?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The [nodeInfo] is keyed by the public key, old node info with the same public key will be replaced by the new node info.
|
* The [nodeInfo] is keyed by the public key, old node info with the same public key will be replaced by the new node info.
|
||||||
* @param nodeInfo node info to be stored
|
* @param signedNodeInfo signed node info data to be stored
|
||||||
* @param signature (optional) signature associated with the node info
|
|
||||||
* @return hash for the newly created node info entry
|
* @return hash for the newly created node info entry
|
||||||
*/
|
*/
|
||||||
fun putNodeInfo(nodeInfo: NodeInfo, signature: DigitalSignature? = null): SecureHash
|
fun putNodeInfo(signedNodeInfo: SignedData<NodeInfo>): SecureHash
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores the signature for the given node info hash.
|
|
||||||
* @param nodeInfoHash node info hash which signature corresponds to
|
|
||||||
* @param signature signature for the node info
|
|
||||||
*/
|
|
||||||
fun signNodeInfo(nodeInfoHash: SecureHash, signature: DigitalSignature.WithKey)
|
|
||||||
}
|
}
|
@ -98,7 +98,7 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
|
|||||||
session.createQuery(query).resultList.first()
|
session.createQuery(query).resultList.first()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDetachedSignedAndValidNodeInfoHashes(): List<SecureHash> = database.transaction {
|
override fun getDetachedAndValidNodeInfoHashes(): List<SecureHash> = database.transaction {
|
||||||
val builder = session.criteriaBuilder
|
val builder = session.criteriaBuilder
|
||||||
// Get signed NodeInfoEntities
|
// Get signed NodeInfoEntities
|
||||||
val query = builder.createQuery(NodeInfoEntity::class.java).run {
|
val query = builder.createQuery(NodeInfoEntity::class.java).run {
|
||||||
|
@ -5,16 +5,11 @@ import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRe
|
|||||||
import com.r3.corda.networkmanage.common.persistence.entity.NodeInfoEntity
|
import com.r3.corda.networkmanage.common.persistence.entity.NodeInfoEntity
|
||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
import com.r3.corda.networkmanage.common.utils.hashString
|
import com.r3.corda.networkmanage.common.utils.hashString
|
||||||
import net.corda.core.crypto.DigitalSignature
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.SignedData
|
import net.corda.core.crypto.SignedData
|
||||||
import net.corda.core.crypto.sha256
|
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.serialization.SerializedBytes
|
import net.corda.core.serialization.SerializedBytes
|
||||||
import net.corda.core.serialization.serialize
|
|
||||||
import net.corda.node.utilities.CordaPersistence
|
import net.corda.node.utilities.CordaPersistence
|
||||||
import org.hibernate.Session
|
|
||||||
import org.hibernate.jpa.QueryHints
|
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
|
|
||||||
@ -22,7 +17,8 @@ import java.sql.Connection
|
|||||||
* Database implementation of the [NetworkMapStorage] interface
|
* Database implementation of the [NetworkMapStorage] interface
|
||||||
*/
|
*/
|
||||||
class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeInfoStorage {
|
class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeInfoStorage {
|
||||||
override fun putNodeInfo(nodeInfo: NodeInfo, signature: DigitalSignature?): SecureHash = database.transaction(Connection.TRANSACTION_SERIALIZABLE) {
|
override fun putNodeInfo(signedNodeInfo: SignedData<NodeInfo>): SecureHash = database.transaction(Connection.TRANSACTION_SERIALIZABLE) {
|
||||||
|
val nodeInfo = signedNodeInfo.verified()
|
||||||
val publicKeyHash = nodeInfo.legalIdentities.first().owningKey.hashString()
|
val publicKeyHash = nodeInfo.legalIdentities.first().owningKey.hashString()
|
||||||
val request = singleRequestWhere(CertificateDataEntity::class.java) { builder, path ->
|
val request = singleRequestWhere(CertificateDataEntity::class.java) { builder, path ->
|
||||||
val certPublicKeyHashEq = builder.equal(path.get<String>(CertificateDataEntity::publicKeyHash.name), publicKeyHash)
|
val certPublicKeyHashEq = builder.equal(path.get<String>(CertificateDataEntity::publicKeyHash.name), publicKeyHash)
|
||||||
@ -31,7 +27,7 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
|
|||||||
}
|
}
|
||||||
request ?: throw IllegalArgumentException("CSR data missing for provided node info: $nodeInfo")
|
request ?: throw IllegalArgumentException("CSR data missing for provided node info: $nodeInfo")
|
||||||
/*
|
/*
|
||||||
* Delete any previous [HashedNodeInfo] instance for this CSR
|
* Delete any previous [NodeInfoEntity] instance for this CSR
|
||||||
* Possibly it should be moved at the network signing process at the network signing process
|
* Possibly it should be moved at the network signing process at the network signing process
|
||||||
* as for a while the network map will have invalid entries (i.e. hashes for node info which have been
|
* as for a while the network map will have invalid entries (i.e. hashes for node info which have been
|
||||||
* removed). Either way, there will be a period of time when the network map data will be invalid
|
* removed). Either way, there will be a period of time when the network map data will be invalid
|
||||||
@ -40,18 +36,19 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
|
|||||||
deleteRequest(NodeInfoEntity::class.java) { builder, path ->
|
deleteRequest(NodeInfoEntity::class.java) { builder, path ->
|
||||||
builder.equal(path.get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name), request.certificateSigningRequest)
|
builder.equal(path.get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name), request.certificateSigningRequest)
|
||||||
}
|
}
|
||||||
val serializedNodeInfo = nodeInfo.serialize().bytes
|
val hash = signedNodeInfo.raw.hash
|
||||||
val hash = serializedNodeInfo.sha256()
|
|
||||||
val hashedNodeInfo = NodeInfoEntity(
|
val hashedNodeInfo = NodeInfoEntity(
|
||||||
nodeInfoHash = hash.toString(),
|
nodeInfoHash = hash.toString(),
|
||||||
certificateSigningRequest = request.certificateSigningRequest,
|
certificateSigningRequest = request.certificateSigningRequest,
|
||||||
nodeInfoBytes = serializedNodeInfo,
|
nodeInfoBytes = signedNodeInfo.raw.bytes,
|
||||||
signatureBytes = signature?.bytes)
|
signatureBytes = signedNodeInfo.sig.bytes,
|
||||||
|
signaturePublicKeyBytes = signedNodeInfo.sig.by.encoded,
|
||||||
|
signaturePublicKeyAlgorithm = signedNodeInfo.sig.by.algorithm)
|
||||||
session.save(hashedNodeInfo)
|
session.save(hashedNodeInfo)
|
||||||
hash
|
hash
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSignedNodeInfo(nodeInfoHash: SecureHash): SignedData<NodeInfo>? = database.transaction {
|
override fun getNodeInfo(nodeInfoHash: SecureHash): SignedData<NodeInfo>? = database.transaction {
|
||||||
val nodeInfoEntity = session.find(NodeInfoEntity::class.java, nodeInfoHash.toString())
|
val nodeInfoEntity = session.find(NodeInfoEntity::class.java, nodeInfoHash.toString())
|
||||||
if (nodeInfoEntity?.signatureBytes == null) {
|
if (nodeInfoEntity?.signatureBytes == null) {
|
||||||
null
|
null
|
||||||
@ -60,18 +57,6 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNodeInfo(nodeInfoHash: SecureHash): NodeInfo? = database.transaction {
|
|
||||||
session.find(NodeInfoEntity::class.java, nodeInfoHash.toString())?.nodeInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getUnsignedNodeInfoBytes(): Map<SecureHash, ByteArray> {
|
|
||||||
return getUnsignedNodeInfoEntities().associate { SecureHash.parse(it.nodeInfoHash) to it.nodeInfoBytes }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getUnsignedNodeInfoHashes(): List<SecureHash> {
|
|
||||||
return getUnsignedNodeInfoEntities().map { SecureHash.parse(it.nodeInfoHash) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getCertificatePath(publicKeyHash: SecureHash): CertPath? {
|
override fun getCertificatePath(publicKeyHash: SecureHash): CertPath? {
|
||||||
return database.transaction {
|
return database.transaction {
|
||||||
val builder = session.criteriaBuilder
|
val builder = session.criteriaBuilder
|
||||||
@ -86,44 +71,4 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
|
|||||||
session.createQuery(query).uniqueResultOptional().orElseGet { null }?.let { buildCertPath(it) }
|
session.createQuery(query).uniqueResultOptional().orElseGet { null }?.let { buildCertPath(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun signNodeInfo(nodeInfoHash: SecureHash, signature: DigitalSignature.WithKey) {
|
|
||||||
database.transaction {
|
|
||||||
val nodeInfoEntity = session.find(NodeInfoEntity::class.java, nodeInfoHash.toString())
|
|
||||||
if (nodeInfoEntity != null) {
|
|
||||||
session.merge(nodeInfoEntity.copy(
|
|
||||||
signatureBytes = signature.bytes,
|
|
||||||
signaturePublicKeyAlgorithm = signature.by.algorithm,
|
|
||||||
signaturePublicKeyBytes = signature.by.encoded
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getUnsignedNodeInfoEntities(): List<NodeInfoEntity> = database.transaction {
|
|
||||||
val builder = session.criteriaBuilder
|
|
||||||
// Retrieve all unsigned NodeInfoHash
|
|
||||||
val query = builder.createQuery(NodeInfoEntity::class.java).run {
|
|
||||||
from(NodeInfoEntity::class.java).run {
|
|
||||||
where(builder.and(builder.isNull(get<ByteArray>(NodeInfoEntity::signatureBytes.name))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Retrieve them together with their CSR
|
|
||||||
val (hintKey, hintValue) = getNodeInfoWithCsrHint(session)
|
|
||||||
val unsigned = session.createQuery(query).setHint(hintKey, hintValue).resultList
|
|
||||||
// Get only those that are valid
|
|
||||||
unsigned.filter({
|
|
||||||
val certificateStatus = it.certificateSigningRequest?.certificateData?.certificateStatus
|
|
||||||
certificateStatus == CertificateStatus.VALID
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates Hibernate query hint for pulling [CertificateSigningRequestEntity] when querying for [NodeInfoEntity]
|
|
||||||
*/
|
|
||||||
private fun getNodeInfoWithCsrHint(session: Session): Pair<String, Any> {
|
|
||||||
val graph = session.createEntityGraph(NodeInfoEntity::class.java)
|
|
||||||
graph.addAttributeNodes(NodeInfoEntity::certificateSigningRequest.name)
|
|
||||||
return QueryHints.HINT_LOADGRAPH to graph
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -31,7 +31,7 @@ class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage,
|
|||||||
fun signNetworkMap() {
|
fun signNetworkMap() {
|
||||||
val currentSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
val currentSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
||||||
val currentNetworkMapValidNodeInfo = networkMapStorage.getCurrentNetworkMapNodeInfoHashes(listOf(CertificateStatus.VALID))
|
val currentNetworkMapValidNodeInfo = networkMapStorage.getCurrentNetworkMapNodeInfoHashes(listOf(CertificateStatus.VALID))
|
||||||
val detachedValidNodeInfo = networkMapStorage.getDetachedSignedAndValidNodeInfoHashes()
|
val detachedValidNodeInfo = networkMapStorage.getDetachedAndValidNodeInfoHashes()
|
||||||
val nodeInfoHashes = currentNetworkMapValidNodeInfo + detachedValidNodeInfo
|
val nodeInfoHashes = currentNetworkMapValidNodeInfo + detachedValidNodeInfo
|
||||||
val networkParameters = networkMapStorage.getLatestNetworkParameters()
|
val networkParameters = networkMapStorage.getLatestNetworkParameters()
|
||||||
val networkMap = NetworkMap(nodeInfoHashes.map { it.toString() }, networkParameters.serialize().hash.toString())
|
val networkMap = NetworkMap(nodeInfoHashes.map { it.toString() }, networkParameters.serialize().hash.toString())
|
||||||
|
@ -79,7 +79,7 @@ class DoormanServer(hostAndPort: NetworkHostAndPort, private vararg val webServi
|
|||||||
webServices.forEach { register(it) }
|
webServices.forEach { register(it) }
|
||||||
}
|
}
|
||||||
val jerseyServlet = ServletHolder(ServletContainer(resourceConfig)).apply { initOrder = 0 }// Initialise at server start
|
val jerseyServlet = ServletHolder(ServletContainer(resourceConfig)).apply { initOrder = 0 }// Initialise at server start
|
||||||
addServlet(jerseyServlet, "/api/*")
|
addServlet(jerseyServlet, "/*")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,7 +184,7 @@ fun startDoorman(hostAndPort: NetworkHostAndPort,
|
|||||||
val networkMapStorage = PersistentNetworkMapStorage(database)
|
val networkMapStorage = PersistentNetworkMapStorage(database)
|
||||||
val nodeInfoStorage = PersistentNodeInfoStorage(database)
|
val nodeInfoStorage = PersistentNodeInfoStorage(database)
|
||||||
|
|
||||||
val doorman = DoormanServer(hostAndPort, RegistrationWebService(requestProcessor, DoormanServer.serverStatus), NodeInfoWebService(nodeInfoStorage, networkMapStorage, signer))
|
val doorman = DoormanServer(hostAndPort, RegistrationWebService(requestProcessor, DoormanServer.serverStatus), NodeInfoWebService(nodeInfoStorage, networkMapStorage))
|
||||||
doorman.start()
|
doorman.start()
|
||||||
|
|
||||||
val networkMapSigner = if (signer != null) NetworkMapSigner(networkMapStorage, signer) else null
|
val networkMapSigner = if (signer != null) NetworkMapSigner(networkMapStorage, signer) else null
|
||||||
|
@ -3,12 +3,10 @@ package com.r3.corda.networkmanage.doorman.webservice
|
|||||||
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
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.hashString
|
import com.r3.corda.networkmanage.common.utils.hashString
|
||||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService.Companion.NETWORK_MAP_PATH
|
||||||
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService.Companion.networkMapPath
|
|
||||||
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.crypto.SignedData
|
import net.corda.core.crypto.SignedData
|
||||||
import net.corda.core.node.NetworkParameters
|
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
@ -23,42 +21,39 @@ import javax.ws.rs.core.Response
|
|||||||
import javax.ws.rs.core.Response.ok
|
import javax.ws.rs.core.Response.ok
|
||||||
import javax.ws.rs.core.Response.status
|
import javax.ws.rs.core.Response.status
|
||||||
|
|
||||||
@Path(networkMapPath)
|
@Path(NETWORK_MAP_PATH)
|
||||||
class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage,
|
class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage,
|
||||||
private val networkMapStorage: NetworkMapStorage,
|
private val networkMapStorage: NetworkMapStorage) {
|
||||||
private val signer: LocalSigner? = null) {
|
|
||||||
companion object {
|
companion object {
|
||||||
const val networkMapPath = "network-map"
|
const val NETWORK_MAP_PATH = "network-map"
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("register")
|
@Path("publish")
|
||||||
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
|
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
fun registerNode(input: InputStream): Response {
|
fun registerNode(input: InputStream): Response {
|
||||||
// TODO: Use JSON instead.
|
|
||||||
val registrationData = input.readBytes().deserialize<SignedData<NodeInfo>>()
|
val registrationData = input.readBytes().deserialize<SignedData<NodeInfo>>()
|
||||||
|
|
||||||
val nodeInfo = registrationData.verified()
|
val nodeInfo = registrationData.verified()
|
||||||
val digitalSignature = registrationData.sig
|
|
||||||
|
|
||||||
val certPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(digitalSignature.by.hashString()))
|
val certPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(nodeInfo.legalIdentitiesAndCerts.first().certPath.certificates.first().publicKey.hashString()))
|
||||||
return if (certPath != null) {
|
return if (certPath != null) {
|
||||||
try {
|
try {
|
||||||
val serializedNodeInfo = nodeInfo.serialize().bytes
|
|
||||||
val nodeCAPubKey = certPath.certificates.first().publicKey
|
val nodeCAPubKey = certPath.certificates.first().publicKey
|
||||||
// Validate node public key
|
// Validate node public key
|
||||||
nodeInfo.legalIdentitiesAndCerts.forEach {
|
nodeInfo.legalIdentitiesAndCerts.forEach {
|
||||||
require(it.certPath.certificates.any { it.publicKey == nodeCAPubKey })
|
require(it.certPath.certificates.any { it.publicKey == nodeCAPubKey })
|
||||||
}
|
}
|
||||||
require(Crypto.doVerify(nodeCAPubKey, digitalSignature.bytes, serializedNodeInfo))
|
val digitalSignature = registrationData.sig
|
||||||
// Store the NodeInfo and notify registration listener
|
require(Crypto.doVerify(nodeCAPubKey, digitalSignature.bytes, registrationData.raw.bytes))
|
||||||
nodeInfoStorage.putNodeInfo(nodeInfo, signer?.sign(serializedNodeInfo)?.signature)
|
// Store the NodeInfo
|
||||||
|
nodeInfoStorage.putNodeInfo(registrationData)
|
||||||
ok()
|
ok()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// Catch exceptions thrown by signature verification.
|
// Catch exceptions thrown by signature verification.
|
||||||
when (e) {
|
when (e) {
|
||||||
is IllegalArgumentException, is InvalidKeyException, is SignatureException -> status(Response.Status.UNAUTHORIZED).entity(e.message)
|
is IllegalArgumentException, is InvalidKeyException, is SignatureException -> status(Response.Status.UNAUTHORIZED).entity(e.message)
|
||||||
// Rethrow e if its not one of the expected exception, the server will return http 500 internal error.
|
// Rethrow e if its not one of the expected exception, the server will return http 500 internal error.
|
||||||
else -> throw e
|
else -> throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,9 +71,12 @@ class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage,
|
|||||||
@GET
|
@GET
|
||||||
@Path("{nodeInfoHash}")
|
@Path("{nodeInfoHash}")
|
||||||
fun getNodeInfo(@PathParam("nodeInfoHash") nodeInfoHash: String): Response {
|
fun getNodeInfo(@PathParam("nodeInfoHash") nodeInfoHash: String): Response {
|
||||||
return nodeInfoStorage.getSignedNodeInfo(SecureHash.parse(nodeInfoHash))?.let {
|
val nodeInfo = nodeInfoStorage.getNodeInfo(SecureHash.parse(nodeInfoHash))
|
||||||
ok(it.serialize().bytes).build()
|
return if (nodeInfo != null) {
|
||||||
} ?: status(Response.Status.NOT_FOUND).build()
|
ok(nodeInfo.serialize().bytes).build()
|
||||||
|
} else {
|
||||||
|
status(Response.Status.NOT_FOUND).build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.r3.corda.networkmanage.hsm
|
package com.r3.corda.networkmanage.hsm
|
||||||
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.PersistentNetworkMapStorage
|
import com.r3.corda.networkmanage.common.persistence.PersistentNetworkMapStorage
|
||||||
import com.r3.corda.networkmanage.common.persistence.PersistentNodeInfoStorage
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.SchemaService
|
import com.r3.corda.networkmanage.common.persistence.SchemaService
|
||||||
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
|
||||||
@ -32,10 +31,8 @@ fun run(parameters: Parameters) {
|
|||||||
}, SchemaService())
|
}, SchemaService())
|
||||||
val csrStorage = DBSignedCertificateRequestStorage(database)
|
val csrStorage = DBSignedCertificateRequestStorage(database)
|
||||||
val networkMapStorage = PersistentNetworkMapStorage(database)
|
val networkMapStorage = PersistentNetworkMapStorage(database)
|
||||||
val nodeInfoStorage = PersistentNodeInfoStorage(database)
|
|
||||||
val hsmNetworkMapSigningThread = HsmNetworkMapSigner(
|
val hsmNetworkMapSigningThread = HsmNetworkMapSigner(
|
||||||
networkMapStorage,
|
networkMapStorage,
|
||||||
nodeInfoStorage,
|
|
||||||
networkMapCertificateName,
|
networkMapCertificateName,
|
||||||
networkMapPrivateKeyPass,
|
networkMapPrivateKeyPass,
|
||||||
keyStorePass,
|
keyStorePass,
|
||||||
|
@ -93,7 +93,7 @@ class Authenticator(private val provider: CryptoServerProvider,
|
|||||||
/*
|
/*
|
||||||
* Configuration class for [CryptoServerProvider]
|
* Configuration class for [CryptoServerProvider]
|
||||||
*/
|
*/
|
||||||
internal data class CryptoServerProviderConfig(
|
data class CryptoServerProviderConfig(
|
||||||
val Device: String = "3001@127.0.0.1",
|
val Device: String = "3001@127.0.0.1",
|
||||||
val ConnectionTimeout: Int = 30000,
|
val ConnectionTimeout: Int = 30000,
|
||||||
val Timeout: Int = 60000,
|
val Timeout: Int = 60000,
|
||||||
@ -113,6 +113,10 @@ fun Parameters.createProvider(): CryptoServerProvider {
|
|||||||
KeyGroup = keyGroup,
|
KeyGroup = keyGroup,
|
||||||
KeySpecifier = keySpecifier
|
KeySpecifier = keySpecifier
|
||||||
)
|
)
|
||||||
|
return createProvider(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createProvider(config: CryptoServerProviderConfig): CryptoServerProvider {
|
||||||
val cfgBuffer = ByteArrayOutputStream()
|
val cfgBuffer = ByteArrayOutputStream()
|
||||||
val writer = cfgBuffer.writer(Charsets.UTF_8)
|
val writer = cfgBuffer.writer(Charsets.UTF_8)
|
||||||
for (property in CryptoServerProviderConfig::class.memberProperties) {
|
for (property in CryptoServerProviderConfig::class.memberProperties) {
|
||||||
|
@ -31,7 +31,7 @@ class KeyCertificateGenerator(private val authenticator: Authenticator,
|
|||||||
* @param parentPrivateKeyPassword password for the parent private key
|
* @param parentPrivateKeyPassword password for the parent private key
|
||||||
* @param validDays days of certificate validity
|
* @param validDays days of certificate validity
|
||||||
*/
|
*/
|
||||||
fun generateAllCertificates(keyStorePassword: String?,
|
fun generateAllCertificates(keyStorePassword: String? = null,
|
||||||
intermediateCertificatesCredentials: List<CertificateNameAndPass>,
|
intermediateCertificatesCredentials: List<CertificateNameAndPass>,
|
||||||
parentCertificateName: String,
|
parentCertificateName: String,
|
||||||
parentPrivateKeyPassword: String,
|
parentPrivateKeyPassword: String,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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.persistence.NetworkMapStorage
|
||||||
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
|
|
||||||
import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
|
import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
|
||||||
import com.r3.corda.networkmanage.common.signer.SignatureAndCertPath
|
import com.r3.corda.networkmanage.common.signer.SignatureAndCertPath
|
||||||
import com.r3.corda.networkmanage.common.signer.Signer
|
import com.r3.corda.networkmanage.common.signer.Signer
|
||||||
@ -15,15 +15,15 @@ import net.corda.core.utilities.minutes
|
|||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.util.*
|
import java.util.concurrent.Executors
|
||||||
import kotlin.concurrent.fixedRateTimer
|
import java.util.concurrent.ScheduledExecutorService
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates logic for periodic network map signing execution.
|
* Encapsulates logic for periodic network map signing execution.
|
||||||
* It uses HSM as the signing entity with keys and certificates specified at the construction time.
|
* It uses HSM as the signing entity with keys and certificates specified at the construction time.
|
||||||
*/
|
*/
|
||||||
class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage,
|
class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage,
|
||||||
private val nodeInfoStorage: NodeInfoStorage,
|
|
||||||
private val caCertificateKeyName: String,
|
private val caCertificateKeyName: String,
|
||||||
private val caPrivateKeyPass: String,
|
private val caPrivateKeyPass: String,
|
||||||
private val keyStorePassword: String?,
|
private val keyStorePassword: String?,
|
||||||
@ -33,45 +33,28 @@ class HsmNetworkMapSigner(networkMapStorage: NetworkMapStorage,
|
|||||||
companion object {
|
companion object {
|
||||||
val log = loggerFor<HsmNetworkMapSigner>()
|
val log = loggerFor<HsmNetworkMapSigner>()
|
||||||
val DEFAULT_SIGNING_PERIOD_MS = 10.minutes
|
val DEFAULT_SIGNING_PERIOD_MS = 10.minutes
|
||||||
|
|
||||||
|
private val TERMINATION_TIMEOUT_SEC = 2L
|
||||||
}
|
}
|
||||||
|
|
||||||
private val networkMapSigner = NetworkMapSigner(networkMapStorage, this)
|
private val networkMapSigner = NetworkMapSigner(networkMapStorage, this)
|
||||||
private var fixedRateTimer: Timer? = null
|
private lateinit var scheduledExecutor: ScheduledExecutorService
|
||||||
|
|
||||||
fun start(): HsmNetworkMapSigner {
|
fun start(): HsmNetworkMapSigner {
|
||||||
stop()
|
val signingPeriodMillis = signingPeriod.toMillis()
|
||||||
fixedRateTimer = fixedRateTimer(
|
scheduledExecutor = Executors.newSingleThreadScheduledExecutor()
|
||||||
name = "Network Map Signing Thread",
|
scheduledExecutor.scheduleAtFixedRate({
|
||||||
period = signingPeriod.toMillis(),
|
try {
|
||||||
action = {
|
networkMapSigner.signNetworkMap()
|
||||||
try {
|
} catch (exception: Exception) {
|
||||||
signNodeInfo()
|
log.warn("Exception thrown while signing network map", exception)
|
||||||
networkMapSigner.signNetworkMap()
|
}
|
||||||
} catch (exception: Exception) {
|
}, signingPeriodMillis, signingPeriodMillis, TimeUnit.MILLISECONDS)
|
||||||
log.warn("Exception thrown while signing network map", exception)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stop() {
|
fun stop() {
|
||||||
fixedRateTimer?.cancel()
|
MoreExecutors.shutdownAndAwaitTermination(scheduledExecutor, TERMINATION_TIMEOUT_SEC, TimeUnit.SECONDS)
|
||||||
}
|
|
||||||
|
|
||||||
private fun signNodeInfo() {
|
|
||||||
// Retrieve data
|
|
||||||
val nodeInfoBytes = nodeInfoStorage.getUnsignedNodeInfoBytes()
|
|
||||||
// Authenticate and sign
|
|
||||||
authenticator.connectAndAuthenticate { provider, _ ->
|
|
||||||
val keyStore = X509Utilities.getAndInitializeKeyStore(provider, keyStorePassword)
|
|
||||||
val caCertificateChain = keyStore.getCertificateChain(caCertificateKeyName)
|
|
||||||
val caKey = keyStore.getKey(caCertificateKeyName, caPrivateKeyPass.toCharArray()) as PrivateKey
|
|
||||||
for ((nodeInfoHash, bytes) in nodeInfoBytes) {
|
|
||||||
val signature = signData(bytes, KeyPair(caCertificateChain.first().publicKey, caKey), provider)
|
|
||||||
verify(bytes, signature, caCertificateChain.first().publicKey)
|
|
||||||
nodeInfoStorage.signNodeInfo(nodeInfoHash, signature)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,28 +23,6 @@ abstract class TestBase {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val testSerialization = SerializationEnvironmentRule()
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
|
|
||||||
protected fun certificateSigningRequestEntity(
|
|
||||||
requestId: String = SecureHash.randomSHA256().toString(),
|
|
||||||
status: RequestStatus = RequestStatus.NEW,
|
|
||||||
legalName: String = "TestLegalName",
|
|
||||||
modifiedBy: List<String> = emptyList(),
|
|
||||||
modifiedAt: Instant = Instant.now(),
|
|
||||||
remark: String = "Test remark",
|
|
||||||
certificateData: CertificateDataEntity? = null,
|
|
||||||
requestBytes: ByteArray = ByteArray(0)
|
|
||||||
): CertificateSigningRequestEntity {
|
|
||||||
return CertificateSigningRequestEntity(
|
|
||||||
requestId = requestId,
|
|
||||||
status = status,
|
|
||||||
legalName = legalName,
|
|
||||||
modifiedBy = modifiedBy,
|
|
||||||
modifiedAt = modifiedAt,
|
|
||||||
remark = remark,
|
|
||||||
certificateData = certificateData,
|
|
||||||
requestBytes = requestBytes
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun certificateSigningRequest(
|
protected fun certificateSigningRequest(
|
||||||
requestId: String = SecureHash.randomSHA256().toString(),
|
requestId: String = SecureHash.randomSHA256().toString(),
|
||||||
status: RequestStatus = RequestStatus.NEW,
|
status: RequestStatus = RequestStatus.NEW,
|
||||||
|
@ -7,11 +7,11 @@ import com.r3.corda.networkmanage.common.signer.SignedNetworkMap
|
|||||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
|
import net.corda.core.crypto.SignedData
|
||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.NotaryInfo
|
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.utilities.CertificateType
|
import net.corda.node.utilities.CertificateType
|
||||||
@ -62,15 +62,15 @@ class DBNetworkMapStorageTest : TestBase() {
|
|||||||
val certPath = buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
val certPath = buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||||
requestStorage.putCertificatePath(requestId, certPath, emptyList())
|
requestStorage.putCertificatePath(requestId, certPath, emptyList())
|
||||||
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||||
val nodeInfoHash = nodeInfoStorage.putNodeInfo(nodeInfo)
|
// Put signed node info data
|
||||||
// Some random bytes
|
val nodeInfoBytes = nodeInfo.serialize()
|
||||||
val signature = keyPair.sign(nodeInfo.serialize())
|
val nodeInfoHash = nodeInfoStorage.putNodeInfo(SignedData(nodeInfoBytes, keyPair.sign(nodeInfoBytes)))
|
||||||
nodeInfoStorage.signNodeInfo(nodeInfoHash, signature)
|
|
||||||
|
|
||||||
// Create network parameters
|
// Create network parameters
|
||||||
val networkParametersHash = networkMapStorage.putNetworkParameters(testNetworkParameters(emptyList()))
|
val networkParametersHash = networkMapStorage.putNetworkParameters(testNetworkParameters(emptyList()))
|
||||||
|
|
||||||
val signatureData = SignatureAndCertPath(signature, certPath)
|
val networkMap = NetworkMap(listOf(nodeInfoHash.toString()), networkParametersHash.toString())
|
||||||
|
val signatureData = SignatureAndCertPath(keyPair.sign(networkMap.serialize()), certPath)
|
||||||
val signedNetworkMap = SignedNetworkMap(NetworkMap(listOf(nodeInfoHash.toString()), networkParametersHash.toString()), signatureData)
|
val signedNetworkMap = SignedNetworkMap(NetworkMap(listOf(nodeInfoHash.toString()), networkParametersHash.toString()), signatureData)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -121,7 +121,7 @@ class DBNetworkMapStorageTest : TestBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `getDetachedSignedAndValidNodeInfoHashes returns only valid and signed node info hashes`() {
|
fun `getDetachedAndValidNodeInfoHashes returns only valid and signed node info hashes`() {
|
||||||
// given
|
// given
|
||||||
// Create node info.
|
// Create node info.
|
||||||
val organisationA = "TestA"
|
val organisationA = "TestA"
|
||||||
@ -139,11 +139,11 @@ class DBNetworkMapStorageTest : TestBase() {
|
|||||||
requestStorage.putCertificatePath(requestIdB, certPathB, emptyList())
|
requestStorage.putCertificatePath(requestIdB, certPathB, emptyList())
|
||||||
val nodeInfoA = NodeInfo(listOf(NetworkHostAndPort("my.companyA.com", 1234)), listOf(PartyAndCertificate(certPathA)), 1, serial = 1L)
|
val nodeInfoA = NodeInfo(listOf(NetworkHostAndPort("my.companyA.com", 1234)), listOf(PartyAndCertificate(certPathA)), 1, serial = 1L)
|
||||||
val nodeInfoB = NodeInfo(listOf(NetworkHostAndPort("my.companyB.com", 1234)), listOf(PartyAndCertificate(certPathB)), 1, serial = 1L)
|
val nodeInfoB = NodeInfo(listOf(NetworkHostAndPort("my.companyB.com", 1234)), listOf(PartyAndCertificate(certPathB)), 1, serial = 1L)
|
||||||
val nodeInfoHashA = nodeInfoStorage.putNodeInfo(nodeInfoA)
|
// Put signed node info data
|
||||||
val nodeInfoHashB = nodeInfoStorage.putNodeInfo(nodeInfoB)
|
val nodeInfoABytes = nodeInfoA.serialize()
|
||||||
// Sign node info
|
val nodeInfoBBytes = nodeInfoB.serialize()
|
||||||
nodeInfoStorage.signNodeInfo(nodeInfoHashA, keyPair.sign(nodeInfoA.serialize()))
|
val nodeInfoHashA = nodeInfoStorage.putNodeInfo(SignedData(nodeInfoABytes, keyPair.sign(nodeInfoABytes)))
|
||||||
nodeInfoStorage.signNodeInfo(nodeInfoHashB, keyPair.sign(nodeInfoB.serialize()))
|
val nodeInfoHashB = nodeInfoStorage.putNodeInfo(SignedData(nodeInfoBBytes, keyPair.sign(nodeInfoBBytes)))
|
||||||
|
|
||||||
// Create network parameters
|
// Create network parameters
|
||||||
val networkParametersHash = networkMapStorage.putNetworkParameters(createNetworkParameters())
|
val networkParametersHash = networkMapStorage.putNetworkParameters(createNetworkParameters())
|
||||||
@ -155,7 +155,7 @@ class DBNetworkMapStorageTest : TestBase() {
|
|||||||
networkMapStorage.saveNetworkMap(signedNetworkMap)
|
networkMapStorage.saveNetworkMap(signedNetworkMap)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val detachedHashes = networkMapStorage.getDetachedSignedAndValidNodeInfoHashes()
|
val detachedHashes = networkMapStorage.getDetachedAndValidNodeInfoHashes()
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(1, detachedHashes.size)
|
assertEquals(1, detachedHashes.size)
|
||||||
|
@ -6,12 +6,11 @@ import com.r3.corda.networkmanage.common.utils.hashString
|
|||||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||||
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.crypto.sha256
|
import net.corda.core.crypto.SignedData
|
||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.serialization.deserialize
|
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.utilities.CertificateType
|
import net.corda.node.utilities.CertificateType
|
||||||
@ -25,7 +24,6 @@ import org.junit.Test
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
import kotlin.test.assertTrue
|
|
||||||
|
|
||||||
class PersitenceNodeInfoStorageTest : TestBase() {
|
class PersitenceNodeInfoStorageTest : TestBase() {
|
||||||
private lateinit var requestStorage: CertificationRequestStorage
|
private lateinit var requestStorage: CertificationRequestStorage
|
||||||
@ -72,8 +70,8 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test getNodeInfoHashes`() {
|
fun `test getNodeInfoHash returns correct data`() {
|
||||||
// Create node info.
|
// given
|
||||||
val organisationA = "TestA"
|
val organisationA = "TestA"
|
||||||
val requestIdA = requestStorage.saveRequest(createRequest(organisationA).first)
|
val requestIdA = requestStorage.saveRequest(createRequest(organisationA).first)
|
||||||
requestStorage.approveRequest(requestIdA, "TestUser")
|
requestStorage.approveRequest(requestIdA, "TestUser")
|
||||||
@ -88,22 +86,23 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
|||||||
val certPathB = buildCertPath(clientCertB.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
val certPathB = buildCertPath(clientCertB.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
||||||
requestStorage.putCertificatePath(requestIdB, certPathB, emptyList())
|
requestStorage.putCertificatePath(requestIdB, certPathB, emptyList())
|
||||||
val nodeInfoA = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPathA)), 1, serial = 1L)
|
val nodeInfoA = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPathA)), 1, serial = 1L)
|
||||||
val nodeInfoSame = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPathA)), 1, serial = 1L)
|
|
||||||
val nodeInfoB = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPathB)), 1, serial = 1L)
|
val nodeInfoB = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPathB)), 1, serial = 1L)
|
||||||
|
|
||||||
nodeInfoStorage.putNodeInfo(nodeInfoA)
|
// Put signed node info data
|
||||||
nodeInfoStorage.putNodeInfo(nodeInfoSame)
|
val nodeInfoABytes = nodeInfoA.serialize()
|
||||||
|
val nodeInfoBBytes = nodeInfoB.serialize()
|
||||||
|
nodeInfoStorage.putNodeInfo(SignedData(nodeInfoABytes, keyPair.sign(nodeInfoABytes)))
|
||||||
|
nodeInfoStorage.putNodeInfo(SignedData(nodeInfoBBytes, keyPair.sign(nodeInfoBBytes)))
|
||||||
|
|
||||||
// getNodeInfoHashes should contain 1 hash.
|
// when
|
||||||
assertEquals(listOf(nodeInfoA.serialize().sha256()), nodeInfoStorage.getUnsignedNodeInfoHashes())
|
val persistedNodeInfoA = nodeInfoStorage.getNodeInfo(nodeInfoABytes.hash)
|
||||||
|
val persistedNodeInfoB = nodeInfoStorage.getNodeInfo(nodeInfoBBytes.hash)
|
||||||
|
|
||||||
nodeInfoStorage.putNodeInfo(nodeInfoB)
|
// then
|
||||||
// getNodeInfoHashes should contain 2 hash.
|
assertNotNull(persistedNodeInfoA)
|
||||||
assertEquals(listOf(nodeInfoB.serialize().sha256(), nodeInfoA.serialize().sha256()).sorted(), nodeInfoStorage.getUnsignedNodeInfoHashes().sorted())
|
assertNotNull(persistedNodeInfoB)
|
||||||
|
assertEquals(persistedNodeInfoA!!.verified(), nodeInfoA)
|
||||||
// Test retrieve NodeInfo.
|
assertEquals(persistedNodeInfoB!!.verified(), nodeInfoB)
|
||||||
assertEquals(nodeInfoA, nodeInfoStorage.getNodeInfo(nodeInfoA.serialize().sha256()))
|
|
||||||
assertEquals(nodeInfoB, nodeInfoStorage.getNodeInfo(nodeInfoB.serialize().sha256()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -119,19 +118,21 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
|||||||
|
|
||||||
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||||
val nodeInfoSamePubKey = NodeInfo(listOf(NetworkHostAndPort("my.company2.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
val nodeInfoSamePubKey = NodeInfo(listOf(NetworkHostAndPort("my.company2.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||||
|
val nodeInfoBytes = nodeInfo.serialize()
|
||||||
|
val nodeInfoHash = nodeInfoStorage.putNodeInfo(SignedData(nodeInfoBytes, keyPair.sign(nodeInfoBytes)))
|
||||||
|
assertEquals(nodeInfo, nodeInfoStorage.getNodeInfo(nodeInfoHash)?.verified())
|
||||||
|
|
||||||
nodeInfoStorage.putNodeInfo(nodeInfo)
|
val nodeInfoSamePubKeyBytes = nodeInfoSamePubKey.serialize()
|
||||||
assertEquals(nodeInfo, nodeInfoStorage.getNodeInfo(nodeInfo.serialize().sha256()))
|
|
||||||
|
|
||||||
// This should replace the node info.
|
// This should replace the node info.
|
||||||
nodeInfoStorage.putNodeInfo(nodeInfoSamePubKey)
|
nodeInfoStorage.putNodeInfo(SignedData(nodeInfoSamePubKeyBytes, keyPair.sign(nodeInfoSamePubKeyBytes)))
|
||||||
|
|
||||||
// Old node info should be removed.
|
// Old node info should be removed.
|
||||||
assertNull(nodeInfoStorage.getNodeInfo(nodeInfo.serialize().sha256()))
|
assertNull(nodeInfoStorage.getNodeInfo(nodeInfoHash))
|
||||||
assertEquals(nodeInfoSamePubKey, nodeInfoStorage.getNodeInfo(nodeInfoSamePubKey.serialize().sha256()))
|
assertEquals(nodeInfoSamePubKey, nodeInfoStorage.getNodeInfo(nodeInfoSamePubKeyBytes.hash)?.verified())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `signNodeInfo associates signature to with node info`() {
|
fun `putNodeInfo persists node info data with its signature`() {
|
||||||
// given
|
// given
|
||||||
// Create node info.
|
// Create node info.
|
||||||
val organisation = "Test"
|
val organisation = "Test"
|
||||||
@ -143,38 +144,16 @@ class PersitenceNodeInfoStorageTest : TestBase() {
|
|||||||
requestStorage.putCertificatePath(requestId, certPath, emptyList())
|
requestStorage.putCertificatePath(requestId, certPath, emptyList())
|
||||||
|
|
||||||
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
||||||
val nodeInfoHash = nodeInfoStorage.putNodeInfo(nodeInfo)
|
val nodeInfoBytes = nodeInfo.serialize()
|
||||||
// Some random bytes
|
val signature = keyPair.sign(nodeInfoBytes)
|
||||||
val signature = keyPair.sign(nodeInfo.serialize())
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
nodeInfoStorage.signNodeInfo(nodeInfoHash, signature)
|
val nodeInfoHash = nodeInfoStorage.putNodeInfo(SignedData(nodeInfoBytes, signature))
|
||||||
|
|
||||||
// then
|
// then
|
||||||
val signedNodeInfo = nodeInfoStorage.getSignedNodeInfo(nodeInfoHash)
|
val persistedNodeInfo = nodeInfoStorage.getNodeInfo(nodeInfoHash)
|
||||||
assertEquals(signature, signedNodeInfo?.sig)
|
assertNotNull(persistedNodeInfo)
|
||||||
}
|
assertEquals(nodeInfo, persistedNodeInfo!!.verified())
|
||||||
|
assertEquals(signature, persistedNodeInfo.sig)
|
||||||
@Test
|
|
||||||
fun `getUnsignedNodeInfoBytes return node info bytes`() {
|
|
||||||
// given
|
|
||||||
// Create node info.
|
|
||||||
val organisation = "Test"
|
|
||||||
val requestId = requestStorage.saveRequest(createRequest(organisation).first)
|
|
||||||
requestStorage.approveRequest(requestId, "TestUser")
|
|
||||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
val clientCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, intermediateCACert, intermediateCAKey, CordaX500Name(organisation = organisation, locality = "London", country = "GB"), keyPair.public)
|
|
||||||
val certPath = buildCertPath(clientCert.toX509Certificate(), intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())
|
|
||||||
requestStorage.putCertificatePath(requestId, certPath, emptyList())
|
|
||||||
|
|
||||||
val nodeInfo = NodeInfo(listOf(NetworkHostAndPort("my.company.com", 1234)), listOf(PartyAndCertificate(certPath)), 1, serial = 1L)
|
|
||||||
val nodeInfoHash = nodeInfoStorage.putNodeInfo(nodeInfo)
|
|
||||||
|
|
||||||
// when
|
|
||||||
val nodeInfoBytes = nodeInfoStorage.getUnsignedNodeInfoBytes()
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertTrue(nodeInfoBytes.containsKey(nodeInfoHash))
|
|
||||||
assertEquals(nodeInfo, nodeInfoBytes[nodeInfoHash]?.deserialize()!!)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -32,7 +32,7 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
whenever(networkMapStorage.getCurrentNetworkMap())
|
whenever(networkMapStorage.getCurrentNetworkMap())
|
||||||
.thenReturn(SignedNetworkMap(NetworkMap(signedNodeInfoHashes.map { it.toString() }, "Dummy"), mock()))
|
.thenReturn(SignedNetworkMap(NetworkMap(signedNodeInfoHashes.map { it.toString() }, "Dummy"), mock()))
|
||||||
whenever(networkMapStorage.getCurrentNetworkMapNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes)
|
whenever(networkMapStorage.getCurrentNetworkMapNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes)
|
||||||
whenever(networkMapStorage.getDetachedSignedAndValidNodeInfoHashes()).thenReturn(detachedNodeInfoHashes)
|
whenever(networkMapStorage.getDetachedAndValidNodeInfoHashes()).thenReturn(detachedNodeInfoHashes)
|
||||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkMapParameters)
|
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkMapParameters)
|
||||||
whenever(signer.sign(any())).thenReturn(mock())
|
whenever(signer.sign(any())).thenReturn(mock())
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
// then
|
// then
|
||||||
// Verify networkMapStorage calls
|
// Verify networkMapStorage calls
|
||||||
verify(networkMapStorage).getCurrentNetworkMapNodeInfoHashes(any())
|
verify(networkMapStorage).getCurrentNetworkMapNodeInfoHashes(any())
|
||||||
verify(networkMapStorage).getDetachedSignedAndValidNodeInfoHashes()
|
verify(networkMapStorage).getDetachedAndValidNodeInfoHashes()
|
||||||
verify(networkMapStorage).getLatestNetworkParameters()
|
verify(networkMapStorage).getLatestNetworkParameters()
|
||||||
argumentCaptor<SignedNetworkMap>().apply {
|
argumentCaptor<SignedNetworkMap>().apply {
|
||||||
verify(networkMapStorage).saveNetworkMap(capture())
|
verify(networkMapStorage).saveNetworkMap(capture())
|
||||||
@ -63,7 +63,7 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
val signedNetworkMap = SignedNetworkMap(networkMap, mock())
|
val signedNetworkMap = SignedNetworkMap(networkMap, mock())
|
||||||
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
||||||
whenever(networkMapStorage.getCurrentNetworkMapNodeInfoHashes(any())).thenReturn(emptyList())
|
whenever(networkMapStorage.getCurrentNetworkMapNodeInfoHashes(any())).thenReturn(emptyList())
|
||||||
whenever(networkMapStorage.getDetachedSignedAndValidNodeInfoHashes()).thenReturn(emptyList())
|
whenever(networkMapStorage.getDetachedAndValidNodeInfoHashes()).thenReturn(emptyList())
|
||||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkMapParameters)
|
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkMapParameters)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
@ -4,7 +4,6 @@ import com.nhaarman.mockito_kotlin.any
|
|||||||
import com.nhaarman.mockito_kotlin.mock
|
import com.nhaarman.mockito_kotlin.mock
|
||||||
import com.nhaarman.mockito_kotlin.times
|
import com.nhaarman.mockito_kotlin.times
|
||||||
import com.nhaarman.mockito_kotlin.verify
|
import com.nhaarman.mockito_kotlin.verify
|
||||||
import com.r3.corda.networkmanage.TestBase
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
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.signer.NetworkMap
|
import com.r3.corda.networkmanage.common.signer.NetworkMap
|
||||||
@ -21,10 +20,9 @@ import net.corda.core.serialization.serialize
|
|||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.utilities.CertificateType
|
import net.corda.node.utilities.CertificateType
|
||||||
import net.corda.node.utilities.X509Utilities
|
import net.corda.node.utilities.X509Utilities
|
||||||
import net.corda.nodeapi.internal.serialization.*
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme
|
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -34,7 +32,12 @@ import javax.ws.rs.core.MediaType
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
class NodeInfoWebServiceTest : TestBase() {
|
class NodeInfoWebServiceTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val testSerialization = SerializationEnvironmentRule(true)
|
||||||
|
|
||||||
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(locality = "London", organisation = "R3 LTD", country = "GB", commonName = "Corda Node Root CA"), rootCAKey)
|
private val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(locality = "London", organisation = "R3 LTD", country = "GB", commonName = "Corda Node Root CA"), rootCAKey)
|
||||||
private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
@ -57,7 +60,7 @@ class NodeInfoWebServiceTest : TestBase() {
|
|||||||
|
|
||||||
DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(nodeInfoStorage, mock())).use {
|
DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(nodeInfoStorage, mock())).use {
|
||||||
it.start()
|
it.start()
|
||||||
val registerURL = URL("http://${it.hostAndPort}/api/${NodeInfoWebService.networkMapPath}/register")
|
val registerURL = URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}/publish")
|
||||||
val nodeInfoAndSignature = SignedData(nodeInfo.serialize(), digitalSignature).serialize().bytes
|
val nodeInfoAndSignature = SignedData(nodeInfo.serialize(), digitalSignature).serialize().bytes
|
||||||
// Post node info and signature to doorman
|
// Post node info and signature to doorman
|
||||||
doPost(registerURL, nodeInfoAndSignature)
|
doPost(registerURL, nodeInfoAndSignature)
|
||||||
@ -83,7 +86,7 @@ class NodeInfoWebServiceTest : TestBase() {
|
|||||||
|
|
||||||
DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(nodeInfoStorage, mock())).use {
|
DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(nodeInfoStorage, mock())).use {
|
||||||
it.start()
|
it.start()
|
||||||
val registerURL = URL("http://${it.hostAndPort}/api/${NodeInfoWebService.networkMapPath}/register")
|
val registerURL = URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}/publish")
|
||||||
val nodeInfoAndSignature = SignedData(nodeInfo.serialize(), digitalSignature).serialize().bytes
|
val nodeInfoAndSignature = SignedData(nodeInfo.serialize(), digitalSignature).serialize().bytes
|
||||||
// Post node info and signature to doorman
|
// Post node info and signature to doorman
|
||||||
assertFailsWith(IOException::class) {
|
assertFailsWith(IOException::class) {
|
||||||
@ -101,7 +104,7 @@ class NodeInfoWebServiceTest : TestBase() {
|
|||||||
}
|
}
|
||||||
DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(mock(), networkMapStorage)).use {
|
DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(mock(), networkMapStorage)).use {
|
||||||
it.start()
|
it.start()
|
||||||
val conn = URL("http://${it.hostAndPort}/api/${NodeInfoWebService.networkMapPath}").openConnection() as HttpURLConnection
|
val conn = URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}").openConnection() as HttpURLConnection
|
||||||
val signedHashedNetworkMap = conn.inputStream.readBytes().deserialize<SignedNetworkMap>()
|
val signedHashedNetworkMap = conn.inputStream.readBytes().deserialize<SignedNetworkMap>()
|
||||||
verify(networkMapStorage, times(1)).getCurrentNetworkMap()
|
verify(networkMapStorage, times(1)).getCurrentNetworkMap()
|
||||||
assertEquals(signedHashedNetworkMap.networkMap, hashedNetworkMap)
|
assertEquals(signedHashedNetworkMap.networkMap, hashedNetworkMap)
|
||||||
@ -119,19 +122,19 @@ class NodeInfoWebServiceTest : TestBase() {
|
|||||||
|
|
||||||
val nodeInfoStorage: NodeInfoStorage = mock {
|
val nodeInfoStorage: NodeInfoStorage = mock {
|
||||||
val serializedNodeInfo = nodeInfo.serialize()
|
val serializedNodeInfo = nodeInfo.serialize()
|
||||||
on { getSignedNodeInfo(nodeInfoHash) }.thenReturn(SignedData(serializedNodeInfo, keyPair.sign(serializedNodeInfo)))
|
on { getNodeInfo(nodeInfoHash) }.thenReturn(SignedData(serializedNodeInfo, keyPair.sign(serializedNodeInfo)))
|
||||||
}
|
}
|
||||||
|
|
||||||
DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(nodeInfoStorage, mock())).use {
|
DoormanServer(NetworkHostAndPort("localhost", 0), NodeInfoWebService(nodeInfoStorage, mock())).use {
|
||||||
it.start()
|
it.start()
|
||||||
val nodeInfoURL = URL("http://${it.hostAndPort}/api/${NodeInfoWebService.networkMapPath}/$nodeInfoHash")
|
val nodeInfoURL = URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}/$nodeInfoHash")
|
||||||
val conn = nodeInfoURL.openConnection()
|
val conn = nodeInfoURL.openConnection()
|
||||||
val nodeInfoResponse = conn.inputStream.readBytes().deserialize<SignedData<NodeInfo>>()
|
val nodeInfoResponse = conn.inputStream.readBytes().deserialize<SignedData<NodeInfo>>()
|
||||||
verify(nodeInfoStorage, times(1)).getSignedNodeInfo(nodeInfoHash)
|
verify(nodeInfoStorage, times(1)).getNodeInfo(nodeInfoHash)
|
||||||
assertEquals(nodeInfo, nodeInfoResponse.verified())
|
assertEquals(nodeInfo, nodeInfoResponse.verified())
|
||||||
|
|
||||||
assertFailsWith(FileNotFoundException::class) {
|
assertFailsWith(FileNotFoundException::class) {
|
||||||
URL("http://${it.hostAndPort}/api/${NodeInfoWebService.networkMapPath}/${SecureHash.randomSHA256()}").openConnection().getInputStream()
|
URL("http://${it.hostAndPort}/${NodeInfoWebService.NETWORK_MAP_PATH}/${SecureHash.randomSHA256()}").openConnection().getInputStream()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,7 @@ class RegistrationWebServiceTest : TestBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun submitRequest(request: PKCS10CertificationRequest): String {
|
private fun submitRequest(request: PKCS10CertificationRequest): String {
|
||||||
val conn = URL("http://${doormanServer.hostAndPort}/api/certificate").openConnection() as HttpURLConnection
|
val conn = URL("http://${doormanServer.hostAndPort}/certificate").openConnection() as HttpURLConnection
|
||||||
conn.doOutput = true
|
conn.doOutput = true
|
||||||
conn.requestMethod = "POST"
|
conn.requestMethod = "POST"
|
||||||
conn.setRequestProperty("Content-Type", MediaType.APPLICATION_OCTET_STREAM)
|
conn.setRequestProperty("Content-Type", MediaType.APPLICATION_OCTET_STREAM)
|
||||||
@ -178,7 +178,7 @@ class RegistrationWebServiceTest : TestBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun pollForResponse(id: String): PollResponse {
|
private fun pollForResponse(id: String): PollResponse {
|
||||||
val url = URL("http://${doormanServer.hostAndPort}/api/certificate/$id")
|
val url = URL("http://${doormanServer.hostAndPort}/certificate/$id")
|
||||||
val conn = url.openConnection() as HttpURLConnection
|
val conn = url.openConnection() as HttpURLConnection
|
||||||
conn.requestMethod = "GET"
|
conn.requestMethod = "GET"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user