mirror of
https://github.com/corda/corda.git
synced 2025-01-19 03:06:36 +00:00
Adding HSM simulator to integration tests (#92)
* Adding HSM simulator to integration tests * Addressing review comments * Adjusting implementation to the agreed TC setup * Addressing review comments * Addressing review comments - round 2
This commit is contained in:
parent
12124bc0d9
commit
4c7dc58135
network-management
@ -121,6 +121,7 @@ dependencies {
|
|||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||||
testCompile "com.nhaarman:mockito-kotlin:0.6.1"
|
testCompile "com.nhaarman:mockito-kotlin:0.6.1"
|
||||||
|
testCompile "com.spotify:docker-client:8.9.1"
|
||||||
|
|
||||||
compile('com.atlassian.jira:jira-rest-java-client-core:4.0.0') {
|
compile('com.atlassian.jira:jira-rest-java-client-core:4.0.0') {
|
||||||
// The jira client includes jersey-core 1.5 which breaks everything.
|
// The jira client includes jersey-core 1.5 which breaks everything.
|
||||||
|
@ -0,0 +1,126 @@
|
|||||||
|
package com.r3.corda.networkmanage
|
||||||
|
|
||||||
|
import com.spotify.docker.client.DefaultDockerClient
|
||||||
|
import com.spotify.docker.client.DockerClient
|
||||||
|
import com.spotify.docker.client.messages.ContainerConfig
|
||||||
|
import com.spotify.docker.client.messages.HostConfig
|
||||||
|
import com.spotify.docker.client.messages.PortBinding
|
||||||
|
import com.spotify.docker.client.messages.RegistryAuth
|
||||||
|
import net.corda.core.utilities.loggerFor
|
||||||
|
import net.corda.testing.freeLocalHostAndPort
|
||||||
|
import org.junit.Assume.assumeFalse
|
||||||
|
import org.junit.rules.ExternalResource
|
||||||
|
|
||||||
|
data class CryptoUserCredentials(val username: String, val password: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HSM Simulator rule allowing to use the HSM Simulator within the integration tests. It is designed to be used mainly
|
||||||
|
* on the TeamCity, but if the required setup is available it can be executed locally as well.
|
||||||
|
* It will bind to the simulator to the local port
|
||||||
|
* To use it locally, the following pre-requisites need to be met:
|
||||||
|
* 1) Docker engine needs to be installed on the machine
|
||||||
|
* 2) Environment variables (AZURE_CR_USER and AZURE_CR_PASS) are available and hold valid credentials to the corda.azurecr.io
|
||||||
|
* repository
|
||||||
|
* 3) HSM requires Unlimited Strength Jurisdiction extension to be installed on the machine connecting with the HSM box.
|
||||||
|
* See http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
|
||||||
|
*
|
||||||
|
* Since the above setup is not a strong requirement for the integration tests to be executed it is intended that this
|
||||||
|
* rule is used together with the assumption mechanism in tests.
|
||||||
|
*/
|
||||||
|
class HsmSimulator(private val serverAddress: String = DEFAULT_SERVER_ADDRESS,
|
||||||
|
private val imageRepoTag: String = DEFAULT_IMAGE_REPO_TAG,
|
||||||
|
private val imageVersion: String = DEFAULT_IMAGE_VERSION) : ExternalResource() {
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
val DEFAULT_SERVER_ADDRESS = "corda.azurecr.io"
|
||||||
|
val DEFAULT_IMAGE_REPO_TAG = "corda.azurecr.io/network-management/hsm-simulator"
|
||||||
|
val DEFAULT_IMAGE_VERSION = "latest"
|
||||||
|
|
||||||
|
val HSM_SIMULATOR_PORT = "3001/tcp"
|
||||||
|
val CONTAINER_KILL_TIMEOUT_SECONDS = 10
|
||||||
|
|
||||||
|
val CRYPTO_USER = System.getProperty("CRYPTO_USER", "INTEGRATION_TEST")
|
||||||
|
val CRYPTO_PASSWORD = System.getProperty("CRYPTO_PASSWORD", "INTEGRATION_TEST")
|
||||||
|
|
||||||
|
val REGISTRY_USERNAME = System.getenv("AZURE_CR_USER")
|
||||||
|
val REGISTRY_PASSWORD = System.getenv("AZURE_CR_PASS")
|
||||||
|
|
||||||
|
val log = loggerFor<HsmSimulator>()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val localHostAndPortBinding = freeLocalHostAndPort()
|
||||||
|
private lateinit var docker: DockerClient
|
||||||
|
private var containerId: String? = null
|
||||||
|
|
||||||
|
override fun before() {
|
||||||
|
assumeFalse("Docker registry username is not set!. Skipping the test.", REGISTRY_USERNAME.isNullOrBlank())
|
||||||
|
assumeFalse("Docker registry password is not set!. Skipping the test.", REGISTRY_PASSWORD.isNullOrBlank())
|
||||||
|
docker = DefaultDockerClient.fromEnv().build().pullHsmSimulatorImageFromRepository()
|
||||||
|
containerId = docker.createContainer()
|
||||||
|
docker.startHsmSimulatorContainer()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after() {
|
||||||
|
docker.stopAndRemoveHsmSimulatorContainer()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the port at which simulator is listening at.
|
||||||
|
*/
|
||||||
|
val port get(): Int = localHostAndPortBinding.port
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the host IP address, which the simulator is listening at.
|
||||||
|
*/
|
||||||
|
val host get(): String = localHostAndPortBinding.host
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the HSM user credentials. Those are supposed to be preconfigured on the HSM itself. Thus, when
|
||||||
|
* tests are executed these credentials can be used to access HSM crypto user's functionality.
|
||||||
|
* It is assumed that the docker image state has those configured already and they should match the ones returned.
|
||||||
|
*/
|
||||||
|
fun cryptoUserCredentials(): CryptoUserCredentials {
|
||||||
|
return CryptoUserCredentials(CRYPTO_USER, CRYPTO_PASSWORD)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DockerClient.stopAndRemoveHsmSimulatorContainer() {
|
||||||
|
if (containerId != null) {
|
||||||
|
log.debug("Stopping container $containerId...")
|
||||||
|
this.stopContainer(containerId, CONTAINER_KILL_TIMEOUT_SECONDS)
|
||||||
|
log.debug("Removing container $containerId...")
|
||||||
|
this.removeContainer(containerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DockerClient.startHsmSimulatorContainer() {
|
||||||
|
if (containerId != null) {
|
||||||
|
log.debug("Starting container $containerId...")
|
||||||
|
this.startContainer(containerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getImageFullName() = "$imageRepoTag:$imageVersion"
|
||||||
|
|
||||||
|
private fun DockerClient.pullHsmSimulatorImageFromRepository(): DockerClient {
|
||||||
|
this.pull(imageRepoTag,
|
||||||
|
RegistryAuth.builder()
|
||||||
|
.serverAddress(serverAddress)
|
||||||
|
.username(REGISTRY_USERNAME)
|
||||||
|
.password(REGISTRY_PASSWORD)
|
||||||
|
.build())
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DockerClient.createContainer(): String? {
|
||||||
|
val portBindings = mapOf(HSM_SIMULATOR_PORT to listOf(PortBinding.create(localHostAndPortBinding.host, localHostAndPortBinding.port.toString())))
|
||||||
|
val hostConfig = HostConfig.builder().portBindings(portBindings).build()
|
||||||
|
val containerConfig = ContainerConfig.builder()
|
||||||
|
.hostConfig(hostConfig)
|
||||||
|
.portSpecs()
|
||||||
|
.image(getImageFullName())
|
||||||
|
.exposedPorts(HSM_SIMULATOR_PORT)
|
||||||
|
.build()
|
||||||
|
val containerCreation = this.createContainer(containerConfig)
|
||||||
|
return containerCreation.id()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package com.r3.corda.networkmanage.hsm
|
||||||
|
|
||||||
|
import com.nhaarman.mockito_kotlin.any
|
||||||
|
import com.nhaarman.mockito_kotlin.mock
|
||||||
|
import com.nhaarman.mockito_kotlin.whenever
|
||||||
|
import com.r3.corda.networkmanage.HsmSimulator
|
||||||
|
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||||
|
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
||||||
|
import com.r3.corda.networkmanage.hsm.configuration.Parameters
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import java.io.Console
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class HsmTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val hsmSimulator: HsmSimulator = HsmSimulator()
|
||||||
|
|
||||||
|
private var console: Console? = null
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
console = mock()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Authenticator executes the block once user is successfully authenticated`() {
|
||||||
|
// given
|
||||||
|
val parameters = Parameters(
|
||||||
|
dataSourceProperties = mock(),
|
||||||
|
device = "${hsmSimulator.port}@${hsmSimulator.host}",
|
||||||
|
keySpecifier = 1,
|
||||||
|
keyGroup = "*"
|
||||||
|
)
|
||||||
|
whenever(console?.readLine()).thenReturn(hsmSimulator.cryptoUserCredentials().username)
|
||||||
|
whenever(console?.readPassword(any())).thenReturn(hsmSimulator.cryptoUserCredentials().password.toCharArray())
|
||||||
|
val authenticator = Authenticator(parameters.createProvider(), console = console)
|
||||||
|
var executed = false
|
||||||
|
|
||||||
|
// when
|
||||||
|
authenticator.connectAndAuthenticate({ provider, signers ->
|
||||||
|
executed = true
|
||||||
|
})
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertTrue(executed)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user