Added nice extension methods for Path, which are more readable than the static methods from Files

This commit is contained in:
Shams Asari 2016-11-04 11:33:43 +00:00
parent 2bc8f8414d
commit bd89da458b
17 changed files with 135 additions and 129 deletions

View File

@ -44,6 +44,7 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile "org.jetbrains.kotlinx:kotlinx-support-jdk8:0.2"
compile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
// Thread safety annotations // Thread safety annotations

View File

@ -8,6 +8,7 @@ import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors import com.google.common.util.concurrent.MoreExecutors
import com.google.common.util.concurrent.SettableFuture import com.google.common.util.concurrent.SettableFuture
import com.r3corda.core.crypto.newSecureRandom import com.r3corda.core.crypto.newSecureRandom
import kotlinx.support.jdk7.use
import org.slf4j.Logger import org.slf4j.Logger
import rx.Observable import rx.Observable
import rx.subjects.UnicastSubject import rx.subjects.UnicastSubject
@ -15,16 +16,16 @@ import java.io.BufferedInputStream
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.math.BigDecimal import java.math.BigDecimal
import java.nio.file.Files import java.nio.charset.Charset
import java.nio.file.LinkOption import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.OpenOption import java.nio.file.*
import java.nio.file.Path
import java.nio.file.attribute.FileAttribute import java.nio.file.attribute.FileAttribute
import java.time.Duration import java.time.Duration
import java.time.temporal.Temporal import java.time.temporal.Temporal
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
import java.util.concurrent.Executor import java.util.concurrent.Executor
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
import java.util.stream.Stream
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import kotlin.concurrent.withLock import kotlin.concurrent.withLock
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
@ -95,15 +96,27 @@ inline fun <T> SettableFuture<T>.catch(block: () -> T) {
/** Allows you to write code like: Paths.get("someDir") / "subdir" / "filename" but using the Paths API to avoid platform separator problems. */ /** Allows you to write code like: Paths.get("someDir") / "subdir" / "filename" but using the Paths API to avoid platform separator problems. */
operator fun Path.div(other: String): Path = resolve(other) operator fun Path.div(other: String): Path = resolve(other)
fun Path.createDirectory(vararg attrs: FileAttribute<*>): Path = Files.createDirectory(this, *attrs)
fun Path.createDirectories(vararg attrs: FileAttribute<*>): Path = Files.createDirectories(this, *attrs) fun Path.createDirectories(vararg attrs: FileAttribute<*>): Path = Files.createDirectories(this, *attrs)
fun Path.exists(vararg options: LinkOption): Boolean = Files.exists(this, *options) fun Path.exists(vararg options: LinkOption): Boolean = Files.exists(this, *options)
inline fun <R> Path.use(block: (InputStream) -> R): R = Files.newInputStream(this).use(block) fun Path.moveTo(target: Path, vararg options: CopyOption): Path = Files.move(this, target, *options)
inline fun Path.write(createDirs: Boolean = false, vararg options: OpenOption, block: (OutputStream) -> Unit) { fun Path.isRegularFile(vararg options: LinkOption): Boolean = Files.isRegularFile(this, *options)
fun Path.isDirectory(vararg options: LinkOption): Boolean = Files.isDirectory(this, *options)
val Path.size: Long get() = Files.size(this)
inline fun <R> Path.list(block: (Stream<Path>) -> R): R = Files.list(this).use(block)
fun Path.deleteIfExists(): Boolean = Files.deleteIfExists(this)
fun Path.readAll(): ByteArray = Files.readAllBytes(this)
inline fun <R> Path.read(vararg options: OpenOption, block: (InputStream) -> R): R = Files.newInputStream(this, *options).use(block)
inline fun Path.write(createDirs: Boolean = false, vararg options: OpenOption = emptyArray(), block: (OutputStream) -> Unit) {
if (createDirs) { if (createDirs) {
normalize().parent?.createDirectories() normalize().parent?.createDirectories()
} }
Files.newOutputStream(this, *options).use(block) Files.newOutputStream(this, *options).use(block)
} }
inline fun <R> Path.readLines(charset: Charset = UTF_8, block: (Stream<String>) -> R): R = Files.lines(this, charset).use(block)
fun Path.writeLines(lines: Iterable<CharSequence>, charset: Charset = UTF_8, vararg options: OpenOption): Path = Files.write(this, lines, charset, *options)
fun InputStream.copyTo(target: Path, vararg options: CopyOption): Long = Files.copy(this, target, *options)
// Simple infix function to add back null safety that the JDK lacks: timeA until timeB // Simple infix function to add back null safety that the JDK lacks: timeA until timeB
infix fun Temporal.until(endExclusive: Temporal) = Duration.between(this, endExclusive) infix fun Temporal.until(endExclusive: Temporal) = Duration.between(this, endExclusive)
@ -213,23 +226,23 @@ class TransientProperty<out T>(private val initializer: () -> T) {
*/ */
fun extractZipFile(zipPath: Path, toPath: Path) { fun extractZipFile(zipPath: Path, toPath: Path) {
val normalisedToPath = toPath.normalize() val normalisedToPath = toPath.normalize()
if (!Files.exists(normalisedToPath)) normalisedToPath.createDirectories()
Files.createDirectories(normalisedToPath)
ZipInputStream(BufferedInputStream(Files.newInputStream(zipPath))).use { zip -> zipPath.read {
val zip = ZipInputStream(BufferedInputStream(it))
while (true) { while (true) {
val e = zip.nextEntry ?: break val e = zip.nextEntry ?: break
val outPath = normalisedToPath.resolve(e.name) val outPath = normalisedToPath / e.name
// Security checks: we should reject a zip that contains tricksy paths that try to escape toPath. // Security checks: we should reject a zip that contains tricksy paths that try to escape toPath.
if (!outPath.normalize().startsWith(normalisedToPath)) if (!outPath.normalize().startsWith(normalisedToPath))
throw IllegalStateException("ZIP contained a path that resolved incorrectly: ${e.name}") throw IllegalStateException("ZIP contained a path that resolved incorrectly: ${e.name}")
if (e.isDirectory) { if (e.isDirectory) {
Files.createDirectories(outPath) outPath.createDirectories()
continue continue
} }
Files.newOutputStream(outPath).use { out -> outPath.write { out ->
ByteStreams.copy(zip, out) ByteStreams.copy(zip, out)
} }
zip.closeEntry() zip.closeEntry()

View File

@ -1,7 +1,9 @@
package com.r3corda.core.crypto package com.r3corda.core.crypto
import com.r3corda.core.exists
import com.r3corda.core.random63BitValue import com.r3corda.core.random63BitValue
import com.r3corda.core.use import com.r3corda.core.read
import com.r3corda.core.write
import org.bouncycastle.asn1.ASN1Encodable import org.bouncycastle.asn1.ASN1Encodable
import org.bouncycastle.asn1.ASN1EncodableVector import org.bouncycastle.asn1.ASN1EncodableVector
import org.bouncycastle.asn1.DERSequence import org.bouncycastle.asn1.DERSequence
@ -27,7 +29,6 @@ import java.io.FileWriter
import java.io.InputStream import java.io.InputStream
import java.math.BigInteger import java.math.BigInteger
import java.net.InetAddress import java.net.InetAddress
import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.security.* import java.security.*
import java.security.cert.Certificate import java.security.cert.Certificate
@ -135,14 +136,11 @@ object X509Utilities {
fun loadOrCreateKeyStore(keyStoreFilePath: Path, storePassword: String): KeyStore { fun loadOrCreateKeyStore(keyStoreFilePath: Path, storePassword: String): KeyStore {
val pass = storePassword.toCharArray() val pass = storePassword.toCharArray()
val keyStore = KeyStore.getInstance(KEYSTORE_TYPE) val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
if (Files.exists(keyStoreFilePath)) { if (keyStoreFilePath.exists()) {
keyStoreFilePath.use { keyStore.load(it, pass) } keyStoreFilePath.read { keyStore.load(it, pass) }
} else { } else {
keyStore.load(null, pass) keyStore.load(null, pass)
val output = Files.newOutputStream(keyStoreFilePath) keyStoreFilePath.write { keyStore.store(it, pass) }
output.use {
keyStore.store(output, pass)
}
} }
return keyStore return keyStore
} }
@ -157,7 +155,7 @@ object X509Utilities {
fun loadKeyStore(keyStoreFilePath: Path, storePassword: String): KeyStore { fun loadKeyStore(keyStoreFilePath: Path, storePassword: String): KeyStore {
val pass = storePassword.toCharArray() val pass = storePassword.toCharArray()
val keyStore = KeyStore.getInstance(KEYSTORE_TYPE) val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
keyStoreFilePath.use { keyStore.load(it, pass) } keyStoreFilePath.read { keyStore.load(it, pass) }
return keyStore return keyStore
} }
@ -186,10 +184,7 @@ object X509Utilities {
*/ */
fun saveKeyStore(keyStore: KeyStore, keyStoreFilePath: Path, storePassword: String) { fun saveKeyStore(keyStore: KeyStore, keyStoreFilePath: Path, storePassword: String) {
val pass = storePassword.toCharArray() val pass = storePassword.toCharArray()
val output = Files.newOutputStream(keyStoreFilePath) keyStoreFilePath.write { keyStore.store(it, pass) }
output.use {
keyStore.store(output, pass)
}
} }
/** /**

View File

@ -1,5 +1,6 @@
package com.r3corda.core.crypto package com.r3corda.core.crypto
import com.r3corda.core.div
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x500.style.BCStyle import org.bouncycastle.asn1.x500.style.BCStyle
import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralName
@ -11,6 +12,7 @@ import java.io.DataOutputStream
import java.io.IOException import java.io.IOException
import java.net.InetAddress import java.net.InetAddress
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.nio.file.Path
import java.security.PrivateKey import java.security.PrivateKey
import java.security.SecureRandom import java.security.SecureRandom
import java.security.Signature import java.security.Signature
@ -38,18 +40,15 @@ class X509UtilitiesTest {
assertTrue { caCertAndKey.certificate.basicConstraints > 0 } // This returns the signing path length Would be -1 for non-CA certificate assertTrue { caCertAndKey.certificate.basicConstraints > 0 } // This returns the signing path length Would be -1 for non-CA certificate
} }
@Test @Test
fun `load and save a PEM file certificate`() { fun `load and save a PEM file certificate`() {
val tmpCertificateFile = tempFolder.root.toPath().resolve("cacert.pem") val tmpCertificateFile = tempFile("cacert.pem")
val caCertAndKey = X509Utilities.createSelfSignedCACert("Test Cert") val caCertAndKey = X509Utilities.createSelfSignedCACert("Test Cert")
X509Utilities.saveCertificateAsPEMFile(caCertAndKey.certificate, tmpCertificateFile) X509Utilities.saveCertificateAsPEMFile(caCertAndKey.certificate, tmpCertificateFile)
val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile) val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile)
assertEquals(caCertAndKey.certificate, readCertificate) assertEquals(caCertAndKey.certificate, readCertificate)
} }
@Test @Test
fun `create valid server certificate chain`() { fun `create valid server certificate chain`() {
val caCertAndKey = X509Utilities.createSelfSignedCACert("Test CA Cert") val caCertAndKey = X509Utilities.createSelfSignedCACert("Test CA Cert")
@ -84,15 +83,11 @@ class X509UtilitiesTest {
@Test @Test
fun `create full CA keystore`() { fun `create full CA keystore`() {
val tmpKeyStore = tempFolder.root.toPath().resolve("keystore.jks") val tmpKeyStore = tempFile("keystore.jks")
val tmpTrustStore = tempFolder.root.toPath().resolve("truststore.jks") val tmpTrustStore = tempFile("truststore.jks")
// Generate Root and Intermediate CA cert and put both into key store and root ca cert into trust store // Generate Root and Intermediate CA cert and put both into key store and root ca cert into trust store
X509Utilities.createCAKeyStoreAndTrustStore(tmpKeyStore, X509Utilities.createCAKeyStoreAndTrustStore(tmpKeyStore, "keystorepass", "keypass", tmpTrustStore, "trustpass")
"keystorepass",
"keypass",
tmpTrustStore,
"trustpass")
// Load back generated root CA Cert and private key from keystore and check against copy in truststore // Load back generated root CA Cert and private key from keystore and check against copy in truststore
val keyStore = X509Utilities.loadKeyStore(tmpKeyStore, "keystorepass") val keyStore = X509Utilities.loadKeyStore(tmpKeyStore, "keystorepass")
@ -132,12 +127,11 @@ class X509UtilitiesTest {
assertTrue { intermediateVerifier.verify(intermediateSignature) } assertTrue { intermediateVerifier.verify(intermediateSignature) }
} }
@Test @Test
fun `create server certificate in keystore for SSL`() { fun `create server certificate in keystore for SSL`() {
val tmpCAKeyStore = tempFolder.root.toPath().resolve("keystore.jks") val tmpCAKeyStore = tempFile("keystore.jks")
val tmpTrustStore = tempFolder.root.toPath().resolve("truststore.jks") val tmpTrustStore = tempFile("truststore.jks")
val tmpServerKeyStore = tempFolder.root.toPath().resolve("serverkeystore.jks") val tmpServerKeyStore = tempFile("serverkeystore.jks")
// Generate Root and Intermediate CA cert and put both into key store and root ca cert into trust store // Generate Root and Intermediate CA cert and put both into key store and root ca cert into trust store
X509Utilities.createCAKeyStoreAndTrustStore(tmpCAKeyStore, X509Utilities.createCAKeyStoreAndTrustStore(tmpCAKeyStore,
@ -177,9 +171,9 @@ class X509UtilitiesTest {
@Test @Test
fun `create server cert and use in SSL socket`() { fun `create server cert and use in SSL socket`() {
val tmpCAKeyStore = tempFolder.root.toPath().resolve("keystore.jks") val tmpCAKeyStore = tempFile("keystore.jks")
val tmpTrustStore = tempFolder.root.toPath().resolve("truststore.jks") val tmpTrustStore = tempFile("truststore.jks")
val tmpServerKeyStore = tempFolder.root.toPath().resolve("serverkeystore.jks") val tmpServerKeyStore = tempFile("serverkeystore.jks")
// Generate Root and Intermediate CA cert and put both into key store and root ca cert into trust store // Generate Root and Intermediate CA cert and put both into key store and root ca cert into trust store
val caKeyStore = X509Utilities.createCAKeyStoreAndTrustStore(tmpCAKeyStore, val caKeyStore = X509Utilities.createCAKeyStoreAndTrustStore(tmpCAKeyStore,
@ -218,7 +212,6 @@ class X509UtilitiesTest {
serverSocket.sslParameters = serverParams serverSocket.sslParameters = serverParams
serverSocket.useClientMode = false serverSocket.useClientMode = false
val clientSocket = clientSocketFactory.createSocket() as SSLSocket val clientSocket = clientSocketFactory.createSocket() as SSLSocket
val clientParams = SSLParameters(arrayOf("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", val clientParams = SSLParameters(arrayOf("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
@ -285,4 +278,6 @@ class X509UtilitiesTest {
serverSocket.close() serverSocket.close()
assertTrue(done) assertTrue(done)
} }
private fun tempFile(name: String): Path = tempFolder.root.toPath() / name
} }

View File

@ -56,7 +56,6 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
compile "org.jetbrains.kotlinx:kotlinx-support-jdk8:0.2"
compile "com.google.guava:guava:19.0" compile "com.google.guava:guava:19.0"

View File

@ -4,6 +4,7 @@ import com.codahale.metrics.MetricRegistry
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors import com.google.common.util.concurrent.MoreExecutors
import com.google.common.util.concurrent.SettableFuture import com.google.common.util.concurrent.SettableFuture
import com.r3corda.core.*
import com.r3corda.core.crypto.Party import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.X509Utilities import com.r3corda.core.crypto.X509Utilities
import com.r3corda.core.messaging.SingleMessageRecipient import com.r3corda.core.messaging.SingleMessageRecipient
@ -12,7 +13,6 @@ import com.r3corda.core.node.services.*
import com.r3corda.core.node.services.NetworkMapCache.MapChangeType import com.r3corda.core.node.services.NetworkMapCache.MapChangeType
import com.r3corda.core.protocols.ProtocolLogic import com.r3corda.core.protocols.ProtocolLogic
import com.r3corda.core.protocols.ProtocolLogicRefFactory import com.r3corda.core.protocols.ProtocolLogicRefFactory
import com.r3corda.core.seconds
import com.r3corda.core.serialization.SingletonSerializeAsToken import com.r3corda.core.serialization.SingletonSerializeAsToken
import com.r3corda.core.serialization.deserialize import com.r3corda.core.serialization.deserialize
import com.r3corda.core.serialization.serialize import com.r3corda.core.serialization.serialize
@ -47,7 +47,6 @@ import com.r3corda.protocols.sendRequest
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.slf4j.Logger import org.slf4j.Logger
import java.nio.file.FileAlreadyExistsException import java.nio.file.FileAlreadyExistsException
import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.security.KeyPair import java.security.KeyPair
import java.time.Clock import java.time.Clock
@ -468,11 +467,11 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val netwo
// "permissioned". The identity file is what gets distributed and contains the node's legal name along with // "permissioned". The identity file is what gets distributed and contains the node's legal name along with
// the public key. Obviously in a real system this would need to be a certificate chain of some kind to ensure // the public key. Obviously in a real system this would need to be a certificate chain of some kind to ensure
// the legal name is actually validated in some way. // the legal name is actually validated in some way.
val privKeyFile = dir.resolve(privateKeyFileName) val privKeyFile = dir / privateKeyFileName
val pubIdentityFile = dir.resolve(publicKeyFileName) val pubIdentityFile = dir / publicKeyFileName
val identityName = if (serviceName == null) configuration.myLegalName else configuration.myLegalName + "|" + serviceName val identityName = if (serviceName == null) configuration.myLegalName else configuration.myLegalName + "|" + serviceName
val identityAndKey = if (!Files.exists(privKeyFile)) { val identityAndKey = if (!privKeyFile.exists()) {
log.info("Identity key not found, generating fresh key!") log.info("Identity key not found, generating fresh key!")
val keyPair: KeyPair = generateKeyPair() val keyPair: KeyPair = generateKeyPair()
keyPair.serialize().writeToFile(privKeyFile) keyPair.serialize().writeToFile(privKeyFile)
@ -485,12 +484,12 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val netwo
// Check that the identity in the config file matches the identity file we have stored to disk. // Check that the identity in the config file matches the identity file we have stored to disk.
// This is just a sanity check. It shouldn't fail unless the admin has fiddled with the files and messed // This is just a sanity check. It shouldn't fail unless the admin has fiddled with the files and messed
// things up for us. // things up for us.
val myIdentity = Files.readAllBytes(pubIdentityFile).deserialize<Party>() val myIdentity = pubIdentityFile.readAll().deserialize<Party>()
if (myIdentity.name != identityName) if (myIdentity.name != identityName)
throw ConfigurationException("The legal name in the config file doesn't match the stored identity file:" + throw ConfigurationException("The legal name in the config file doesn't match the stored identity file:" +
"${identityName} vs ${myIdentity.name}") "$identityName vs ${myIdentity.name}")
// Load the private key. // Load the private key.
val keyPair = Files.readAllBytes(privKeyFile).deserialize<KeyPair>() val keyPair = privKeyFile.readAll().deserialize<KeyPair>()
Pair(myIdentity, keyPair) Pair(myIdentity, keyPair)
} }
partyKeys += identityAndKey.second partyKeys += identityAndKey.second
@ -500,17 +499,15 @@ abstract class AbstractNode(open val configuration: NodeConfiguration, val netwo
protected open fun generateKeyPair() = com.r3corda.core.crypto.generateKeyPair() protected open fun generateKeyPair() = com.r3corda.core.crypto.generateKeyPair()
protected fun makeAttachmentStorage(dir: Path): NodeAttachmentService { protected fun makeAttachmentStorage(dir: Path): NodeAttachmentService {
val attachmentsDir = dir.resolve("attachments") val attachmentsDir = dir / "attachments"
try { try {
Files.createDirectory(attachmentsDir) attachmentsDir.createDirectory()
} catch (e: FileAlreadyExistsException) { } catch (e: FileAlreadyExistsException) {
} }
return NodeAttachmentService(attachmentsDir, services.monitoringService.metrics) return NodeAttachmentService(attachmentsDir, services.monitoringService.metrics)
} }
protected fun createNodeDir() { protected fun createNodeDir() {
if (!Files.exists(configuration.basedir)) { configuration.basedir.createDirectories()
Files.createDirectories(configuration.basedir)
}
} }
} }

View File

@ -1,6 +1,7 @@
package com.r3corda.node.internal package com.r3corda.node.internal
import com.codahale.metrics.JmxReporter import com.codahale.metrics.JmxReporter
import com.r3corda.core.div
import com.r3corda.core.messaging.SingleMessageRecipient import com.r3corda.core.messaging.SingleMessageRecipient
import com.r3corda.core.node.ServiceHub import com.r3corda.core.node.ServiceHub
import com.r3corda.core.node.services.ServiceInfo import com.r3corda.core.node.services.ServiceInfo
@ -366,7 +367,7 @@ class Node(override val configuration: FullNodeConfiguration, networkMapAddress:
// file that we'll do our best to delete on exit. But if we don't, it'll be overwritten next time. If it already // file that we'll do our best to delete on exit. But if we don't, it'll be overwritten next time. If it already
// exists, we try to take the file lock first before replacing it and if that fails it means we're being started // exists, we try to take the file lock first before replacing it and if that fails it means we're being started
// twice with the same directory: that's a user error and we should bail out. // twice with the same directory: that's a user error and we should bail out.
val pidPath = configuration.basedir.resolve("process-id") val pidPath = configuration.basedir / "process-id"
val file = pidPath.toFile() val file = pidPath.toFile()
if (!file.exists()) { if (!file.exists()) {
file.createNewFile() file.createNewFile()

View File

@ -1,7 +1,7 @@
package com.r3corda.node.services package com.r3corda.node.services
import com.r3corda.core.exists import com.r3corda.core.exists
import com.r3corda.core.use import com.r3corda.core.read
import java.nio.file.Path import java.nio.file.Path
import java.util.* import java.util.*
@ -25,7 +25,7 @@ class PropertiesFileRPCUserService(file: Path) : RPCUserService {
init { init {
_users = if (file.exists()) { _users = if (file.exists()) {
val properties = Properties() val properties = Properties()
file.use { file.read {
properties.load(it) properties.load(it)
} }
properties.map { properties.map {

View File

@ -1,7 +1,10 @@
package com.r3corda.node.services.config package com.r3corda.node.services.config
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.r3corda.core.copyTo
import com.r3corda.core.createDirectories
import com.r3corda.core.crypto.X509Utilities import com.r3corda.core.crypto.X509Utilities
import com.r3corda.core.div
import com.r3corda.core.exists import com.r3corda.core.exists
import com.r3corda.core.utilities.loggerFor import com.r3corda.core.utilities.loggerFor
import com.typesafe.config.Config import com.typesafe.config.Config
@ -29,7 +32,7 @@ object ConfigHelper {
val defaultConfig = ConfigFactory.parseResources("reference.conf", ConfigParseOptions.defaults().setAllowMissing(false)) val defaultConfig = ConfigFactory.parseResources("reference.conf", ConfigParseOptions.defaults().setAllowMissing(false))
val normalisedBaseDir = baseDirectoryPath.normalize() val normalisedBaseDir = baseDirectoryPath.normalize()
val configFile = (configFileOverride?.normalize() ?: normalisedBaseDir.resolve("node.conf")).toFile() val configFile = (configFileOverride?.normalize() ?: normalisedBaseDir / "node.conf").toFile()
val appConfig = ConfigFactory.parseFile(configFile, ConfigParseOptions.defaults().setAllowMissing(allowMissingConfig)) val appConfig = ConfigFactory.parseFile(configFile, ConfigParseOptions.defaults().setAllowMissing(allowMissingConfig))
val overridesMap = HashMap<String, Any?>() // If we do require a few other command line overrides eg for a nicer development experience they would go inside this map. val overridesMap = HashMap<String, Any?>() // If we do require a few other command line overrides eg for a nicer development experience they would go inside this map.
@ -89,10 +92,9 @@ fun Config.getProperties(path: String): Properties {
* the CA certs in Node resources. Then provision KeyStores into certificates folder under node path. * the CA certs in Node resources. Then provision KeyStores into certificates folder under node path.
*/ */
fun NodeSSLConfiguration.configureWithDevSSLCertificate() { fun NodeSSLConfiguration.configureWithDevSSLCertificate() {
Files.createDirectories(certificatesPath) certificatesPath.createDirectories()
if (!trustStorePath.exists()) { if (!trustStorePath.exists()) {
Files.copy(javaClass.classLoader.getResourceAsStream("com/r3corda/node/internal/certificates/cordatruststore.jks"), javaClass.classLoader.getResourceAsStream("com/r3corda/node/internal/certificates/cordatruststore.jks").copyTo(trustStorePath)
trustStorePath)
} }
if (!keyStorePath.exists()) { if (!keyStorePath.exists()) {
val caKeyStore = X509Utilities.loadKeyStore( val caKeyStore = X509Utilities.loadKeyStore(

View File

@ -7,7 +7,7 @@ import com.r3corda.core.crypto.toBase58String
import com.r3corda.core.messaging.MessageRecipients import com.r3corda.core.messaging.MessageRecipients
import com.r3corda.core.messaging.SingleMessageRecipient import com.r3corda.core.messaging.SingleMessageRecipient
import com.r3corda.core.serialization.SingletonSerializeAsToken import com.r3corda.core.serialization.SingletonSerializeAsToken
import com.r3corda.core.use import com.r3corda.core.read
import com.r3corda.node.services.config.NodeSSLConfiguration import com.r3corda.node.services.config.NodeSSLConfiguration
import com.r3corda.node.services.config.configureWithDevSSLCertificate import com.r3corda.node.services.config.configureWithDevSSLCertificate
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
@ -110,10 +110,10 @@ abstract class ArtemisMessagingComponent() : SingletonSerializeAsToken() {
* unfortunately Artemis tends to bury the exception when the password is wrong. * unfortunately Artemis tends to bury the exception when the password is wrong.
*/ */
fun checkStorePasswords() { fun checkStorePasswords() {
config.keyStorePath.use { config.keyStorePath.read {
KeyStore.getInstance("JKS").load(it, config.keyStorePassword.toCharArray()) KeyStore.getInstance("JKS").load(it, config.keyStorePassword.toCharArray())
} }
config.trustStorePath.use { config.trustStorePath.read {
KeyStore.getInstance("JKS").load(it, config.trustStorePassword.toCharArray()) KeyStore.getInstance("JKS").load(it, config.trustStorePassword.toCharArray())
} }
} }

View File

@ -5,9 +5,9 @@ import com.google.common.annotations.VisibleForTesting
import com.google.common.hash.Hashing import com.google.common.hash.Hashing
import com.google.common.hash.HashingInputStream import com.google.common.hash.HashingInputStream
import com.google.common.io.CountingInputStream import com.google.common.io.CountingInputStream
import com.r3corda.core.*
import com.r3corda.core.contracts.Attachment import com.r3corda.core.contracts.Attachment
import com.r3corda.core.crypto.SecureHash import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.extractZipFile
import com.r3corda.core.node.services.AttachmentStorage import com.r3corda.core.node.services.AttachmentStorage
import com.r3corda.core.utilities.loggerFor import com.r3corda.core.utilities.loggerFor
import com.r3corda.node.services.api.AcceptsFileUpload import com.r3corda.node.services.api.AcceptsFileUpload
@ -35,7 +35,7 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
} }
// Just count all non-directories in the attachment store, and assume the admin hasn't dumped any junk there. // Just count all non-directories in the attachment store, and assume the admin hasn't dumped any junk there.
private fun countAttachments() = Files.list(storePath).filter { Files.isRegularFile(it) }.count() private fun countAttachments() = storePath.list { it.filter { it.isRegularFile() }.count() }
/** /**
* If true, newly inserted attachments will be unzipped to a subdirectory of the [storePath]. This is intended for * If true, newly inserted attachments will be unzipped to a subdirectory of the [storePath]. This is intended for
@ -45,7 +45,7 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
@Volatile var automaticallyExtractAttachments = false @Volatile var automaticallyExtractAttachments = false
init { init {
require(Files.isDirectory(storePath)) { "$storePath must be a directory" } require(storePath.isDirectory()) { "$storePath must be a directory" }
} }
class OnDiskHashMismatch(val file: Path, val actual: SecureHash) : Exception() { class OnDiskHashMismatch(val file: Path, val actual: SecureHash) : Exception() {
@ -66,7 +66,7 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
private val counter: CountingInputStream = CountingInputStream(input), private val counter: CountingInputStream = CountingInputStream(input),
private val stream: HashingInputStream = HashingInputStream(Hashing.sha256(), counter)) : FilterInputStream(stream) { private val stream: HashingInputStream = HashingInputStream(Hashing.sha256(), counter)) : FilterInputStream(stream) {
private val expectedSize = Files.size(filePath) private val expectedSize = filePath.size
override fun close() { override fun close() {
super.close() super.close()
@ -94,8 +94,8 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
} }
override fun openAttachment(id: SecureHash): Attachment? { override fun openAttachment(id: SecureHash): Attachment? {
val path = storePath.resolve(id.toString()) val path = storePath / id.toString()
if (!Files.exists(path)) return null if (!path.exists()) return null
return AttachmentImpl(id, path, checkAttachmentsOnLoad) return AttachmentImpl(id, path, checkAttachmentsOnLoad)
} }
@ -103,28 +103,28 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
override fun importAttachment(jar: InputStream): SecureHash { override fun importAttachment(jar: InputStream): SecureHash {
require(jar !is JarInputStream) require(jar !is JarInputStream)
val hs = HashingInputStream(Hashing.sha256(), jar) val hs = HashingInputStream(Hashing.sha256(), jar)
val tmp = storePath.resolve("tmp.${UUID.randomUUID()}") val tmp = storePath / "tmp.${UUID.randomUUID()}"
Files.copy(hs, tmp) hs.copyTo(tmp)
checkIsAValidJAR(tmp) checkIsAValidJAR(tmp)
val id = SecureHash.SHA256(hs.hash().asBytes()) val id = SecureHash.SHA256(hs.hash().asBytes())
val finalPath = storePath.resolve(id.toString()) val finalPath = storePath / id.toString()
try { try {
// Move into place atomically or fail if that isn't possible. We don't want a half moved attachment to // Move into place atomically or fail if that isn't possible. We don't want a half moved attachment to
// be exposed to parallel threads. This gives us thread safety. // be exposed to parallel threads. This gives us thread safety.
if (!Files.exists(finalPath)) { if (!finalPath.exists()) {
log.info("Stored new attachment $id") log.info("Stored new attachment $id")
attachmentCount.inc() attachmentCount.inc()
} else { } else {
log.info("Replacing attachment $id - only bother doing this if you're trying to repair file corruption") log.info("Replacing attachment $id - only bother doing this if you're trying to repair file corruption")
} }
Files.move(tmp, finalPath, StandardCopyOption.ATOMIC_MOVE) tmp.moveTo(finalPath, StandardCopyOption.ATOMIC_MOVE)
} finally { } finally {
Files.deleteIfExists(tmp) tmp.deleteIfExists()
} }
if (automaticallyExtractAttachments) { if (automaticallyExtractAttachments) {
val extractTo = storePath.resolve("$id.jar") val extractTo = storePath / "$id.jar"
try { try {
Files.createDirectory(extractTo) extractTo.createDirectory()
extractZipFile(finalPath, extractTo) extractZipFile(finalPath, extractTo)
} catch(e: FileAlreadyExistsException) { } catch(e: FileAlreadyExistsException) {
log.trace("Did not extract attachment jar to directory because it already exists") log.trace("Did not extract attachment jar to directory because it already exists")
@ -138,9 +138,10 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
private fun checkIsAValidJAR(path: Path) { private fun checkIsAValidJAR(path: Path) {
// Just iterate over the entries with verification enabled: should be good enough to catch mistakes. // Just iterate over the entries with verification enabled: should be good enough to catch mistakes.
JarInputStream(Files.newInputStream(path), true).use { stream -> path.read {
val jar = JarInputStream(it)
while (true) { while (true) {
val cursor = stream.nextJarEntry ?: break val cursor = jar.nextJarEntry ?: break
val entryPath = Paths.get(cursor.name) val entryPath = Paths.get(cursor.name)
// Security check to stop zips trying to escape their rightful place. // Security check to stop zips trying to escape their rightful place.
if (entryPath.isAbsolute || entryPath.normalize() != entryPath || '\\' in cursor.name) if (entryPath.isAbsolute || entryPath.normalize() != entryPath || '\\' in cursor.name)

View File

@ -1,13 +1,12 @@
package com.r3corda.node.utilities.certsigning package com.r3corda.node.utilities.certsigning
import com.r3corda.core.*
import com.r3corda.core.crypto.X509Utilities import com.r3corda.core.crypto.X509Utilities
import com.r3corda.core.crypto.X509Utilities.CORDA_CLIENT_CA import com.r3corda.core.crypto.X509Utilities.CORDA_CLIENT_CA
import com.r3corda.core.crypto.X509Utilities.CORDA_CLIENT_CA_PRIVATE_KEY import com.r3corda.core.crypto.X509Utilities.CORDA_CLIENT_CA_PRIVATE_KEY
import com.r3corda.core.crypto.X509Utilities.CORDA_ROOT_CA import com.r3corda.core.crypto.X509Utilities.CORDA_ROOT_CA
import com.r3corda.core.crypto.X509Utilities.addOrReplaceCertificate import com.r3corda.core.crypto.X509Utilities.addOrReplaceCertificate
import com.r3corda.core.crypto.X509Utilities.addOrReplaceKey import com.r3corda.core.crypto.X509Utilities.addOrReplaceKey
import com.r3corda.core.div
import com.r3corda.core.minutes
import com.r3corda.core.utilities.loggerFor import com.r3corda.core.utilities.loggerFor
import com.r3corda.node.services.config.ConfigHelper import com.r3corda.node.services.config.ConfigHelper
import com.r3corda.node.services.config.FullNodeConfiguration import com.r3corda.node.services.config.FullNodeConfiguration
@ -15,7 +14,6 @@ import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.config.getValue import com.r3corda.node.services.config.getValue
import joptsimple.OptionParser import joptsimple.OptionParser
import java.net.URL import java.net.URL
import java.nio.file.Files
import java.nio.file.Paths import java.nio.file.Paths
import java.security.KeyPair import java.security.KeyPair
import java.security.cert.Certificate import java.security.cert.Certificate
@ -34,7 +32,7 @@ class CertificateSigner(val config: NodeConfiguration, val certService: Certific
} }
fun buildKeyStore() { fun buildKeyStore() {
Files.createDirectories(config.certificatesPath) config.certificatesPath.createDirectories()
val caKeyStore = X509Utilities.loadOrCreateKeyStore(config.keyStorePath, config.keyStorePassword) val caKeyStore = X509Utilities.loadOrCreateKeyStore(config.keyStorePath, config.keyStorePassword)
@ -101,15 +99,15 @@ class CertificateSigner(val config: NodeConfiguration, val certService: Certific
private fun submitCertificateSigningRequest(keyPair: KeyPair): String { private fun submitCertificateSigningRequest(keyPair: KeyPair): String {
val requestIdStore = config.certificatesPath / "certificate-request-id.txt" val requestIdStore = config.certificatesPath / "certificate-request-id.txt"
// Retrieve request id from file if exists, else post a request to server. // Retrieve request id from file if exists, else post a request to server.
return if (!Files.exists(requestIdStore)) { return if (!requestIdStore.exists()) {
val request = X509Utilities.createCertificateSigningRequest(config.myLegalName, config.nearestCity, config.emailAddress, keyPair) val request = X509Utilities.createCertificateSigningRequest(config.myLegalName, config.nearestCity, config.emailAddress, keyPair)
// Post request to signing server via http. // Post request to signing server via http.
val requestId = certService.submitRequest(request) val requestId = certService.submitRequest(request)
// Persists request ID to file in case of node shutdown. // Persists request ID to file in case of node shutdown.
Files.write(requestIdStore, listOf(requestId), Charsets.UTF_8) requestIdStore.writeLines(listOf(requestId))
requestId requestId
} else { } else {
Files.readAllLines(requestIdStore).first() requestIdStore.readLines { it.findFirst().get() }
} }
} }
} }

View File

@ -5,7 +5,7 @@ import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.crypto.sha256 import com.r3corda.core.crypto.sha256
import com.r3corda.core.messaging.SingleMessageRecipient import com.r3corda.core.messaging.SingleMessageRecipient
import com.r3corda.core.node.services.ServiceInfo import com.r3corda.core.node.services.ServiceInfo
import com.r3corda.core.serialization.OpaqueBytes import com.r3corda.core.write
import com.r3corda.node.services.config.NodeConfiguration import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.network.NetworkMapService import com.r3corda.node.services.network.NetworkMapService
import com.r3corda.node.services.persistence.NodeAttachmentService import com.r3corda.node.services.persistence.NodeAttachmentService
@ -18,9 +18,6 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
import java.nio.file.Files
import java.nio.file.StandardOpenOption
import java.security.KeyPair import java.security.KeyPair
import java.util.jar.JarOutputStream import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
@ -103,9 +100,7 @@ class AttachmentTests {
val id = n0.storage.attachments.importAttachment(ByteArrayInputStream(fakeAttachment())) val id = n0.storage.attachments.importAttachment(ByteArrayInputStream(fakeAttachment()))
// Corrupt its store. // Corrupt its store.
val writer = Files.newByteChannel(network.filesystem.getPath("/nodes/0/attachments/$id"), StandardOpenOption.WRITE) network.filesystem.getPath("/nodes/0/attachments/$id").write { it.write(byteArrayOf(99, 99, 99, 99)) }
writer.write(ByteBuffer.wrap(OpaqueBytes.of(99, 99, 99, 99).bits))
writer.close()
// Get n1 to fetch the attachment. Should receive corrupted bytes. // Get n1 to fetch the attachment. Should receive corrupted bytes.
network.runNetwork() network.runNetwork()

View File

@ -4,12 +4,19 @@ import com.codahale.metrics.MetricRegistry
import com.google.common.jimfs.Configuration import com.google.common.jimfs.Configuration
import com.google.common.jimfs.Jimfs import com.google.common.jimfs.Jimfs
import com.r3corda.core.crypto.SecureHash import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.use import com.r3corda.core.crypto.sha256
import com.r3corda.core.div
import com.r3corda.core.read
import com.r3corda.core.readAll
import com.r3corda.core.write
import com.r3corda.node.services.persistence.NodeAttachmentService import com.r3corda.node.services.persistence.NodeAttachmentService
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.nio.charset.Charset import java.nio.charset.Charset
import java.nio.file.* import java.nio.file.FileAlreadyExistsException
import java.nio.file.FileSystem
import java.nio.file.Path
import java.nio.file.StandardOpenOption.WRITE
import java.util.jar.JarEntry import java.util.jar.JarEntry
import java.util.jar.JarOutputStream import java.util.jar.JarOutputStream
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -28,10 +35,10 @@ class NodeAttachmentStorageTest {
@Test @Test
fun `insert and retrieve`() { fun `insert and retrieve`() {
val testJar = makeTestJar() val testJar = makeTestJar()
val expectedHash = SecureHash.sha256(Files.readAllBytes(testJar)) val expectedHash = testJar.readAll().sha256()
val storage = NodeAttachmentService(fs.getPath("/"), MetricRegistry()) val storage = NodeAttachmentService(fs.getPath("/"), MetricRegistry())
val id = testJar.use { storage.importAttachment(it) } val id = testJar.read { storage.importAttachment(it) }
assertEquals(expectedHash, id) assertEquals(expectedHash, id)
assertNull(storage.openAttachment(SecureHash.randomSHA256())) assertNull(storage.openAttachment(SecureHash.randomSHA256()))
@ -48,9 +55,9 @@ class NodeAttachmentStorageTest {
fun `duplicates not allowed`() { fun `duplicates not allowed`() {
val testJar = makeTestJar() val testJar = makeTestJar()
val storage = NodeAttachmentService(fs.getPath("/"), MetricRegistry()) val storage = NodeAttachmentService(fs.getPath("/"), MetricRegistry())
testJar.use { storage.importAttachment(it) } testJar.read { storage.importAttachment(it) }
assertFailsWith<FileAlreadyExistsException> { assertFailsWith<FileAlreadyExistsException> {
testJar.use { storage.importAttachment(it) } testJar.read { storage.importAttachment(it) }
} }
} }
@ -58,15 +65,15 @@ class NodeAttachmentStorageTest {
fun `corrupt entry throws exception`() { fun `corrupt entry throws exception`() {
val testJar = makeTestJar() val testJar = makeTestJar()
val storage = NodeAttachmentService(fs.getPath("/"), MetricRegistry()) val storage = NodeAttachmentService(fs.getPath("/"), MetricRegistry())
val id = testJar.use { storage.importAttachment(it) } val id = testJar.read { storage.importAttachment(it) }
// Corrupt the file in the store. // Corrupt the file in the store.
Files.write(fs.getPath("/", id.toString()), "arggghhhh".toByteArray(), StandardOpenOption.WRITE) fs.getPath("/", id.toString()).write(options = WRITE) { it.write("arggghhhh".toByteArray()) }
val e = assertFailsWith<NodeAttachmentService.OnDiskHashMismatch> { val e = assertFailsWith<NodeAttachmentService.OnDiskHashMismatch> {
storage.openAttachment(id)!!.open().use { it.readBytes() } storage.openAttachment(id)!!.open().use { it.readBytes() }
} }
assertEquals(e.file, storage.storePath.resolve(id.toString())) assertEquals(e.file, storage.storePath / id.toString())
// But if we skip around and read a single entry, no exception is thrown. // But if we skip around and read a single entry, no exception is thrown.
storage.openAttachment(id)!!.openAsJAR().use { storage.openAttachment(id)!!.openAsJAR().use {
@ -78,15 +85,16 @@ class NodeAttachmentStorageTest {
private var counter = 0 private var counter = 0
private fun makeTestJar(): Path { private fun makeTestJar(): Path {
counter++ counter++
val f = fs.getPath("$counter.jar") val file = fs.getPath("$counter.jar")
JarOutputStream(Files.newOutputStream(f)).use { file.write {
it.putNextEntry(JarEntry("test1.txt")) val jar = JarOutputStream(it)
it.write("This is some useful content".toByteArray()) jar.putNextEntry(JarEntry("test1.txt"))
it.closeEntry() jar.write("This is some useful content".toByteArray())
it.putNextEntry(JarEntry("test2.txt")) jar.closeEntry()
it.write("Some more useful content".toByteArray()) jar.putNextEntry(JarEntry("test2.txt"))
it.closeEntry() jar.write("Some more useful content".toByteArray())
jar.closeEntry()
} }
return f return file
} }
} }

View File

@ -2,11 +2,11 @@ package com.r3corda.node.services
import com.google.common.jimfs.Configuration.unix import com.google.common.jimfs.Configuration.unix
import com.google.common.jimfs.Jimfs import com.google.common.jimfs.Jimfs
import com.r3corda.core.writeLines
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.After import org.junit.After
import org.junit.Test import org.junit.Test
import java.nio.file.Files
class PropertiesFileRPCUserServiceTest { class PropertiesFileRPCUserServiceTest {
@ -75,7 +75,7 @@ class PropertiesFileRPCUserServiceTest {
} }
private fun loadWithContents(vararg lines: String): PropertiesFileRPCUserService { private fun loadWithContents(vararg lines: String): PropertiesFileRPCUserService {
Files.write(file, lines.asList()) file.writeLines(lines.asList())
return PropertiesFileRPCUserService(file) return PropertiesFileRPCUserService(file)
} }
} }

View File

@ -6,11 +6,12 @@ import com.nhaarman.mockito_kotlin.mock
import com.r3corda.core.crypto.SecureHash import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.crypto.X509Utilities import com.r3corda.core.crypto.X509Utilities
import com.r3corda.core.div import com.r3corda.core.div
import com.r3corda.core.exists
import com.r3corda.core.readLines
import com.r3corda.node.services.config.NodeConfiguration import com.r3corda.node.services.config.NodeConfiguration
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.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
@ -47,13 +48,13 @@ class CertificateSignerTest {
override val trustStorePassword: String = "trustpass" override val trustStorePassword: String = "trustpass"
} }
assertFalse(Files.exists(config.keyStorePath)) assertFalse(config.keyStorePath.exists())
assertFalse(Files.exists(config.trustStorePath)) assertFalse(config.trustStorePath.exists())
CertificateSigner(config, certService).buildKeyStore() CertificateSigner(config, certService).buildKeyStore()
assertTrue(Files.exists(config.keyStorePath)) assertTrue(config.keyStorePath.exists())
assertTrue(Files.exists(config.trustStorePath)) assertTrue(config.trustStorePath.exists())
X509Utilities.loadKeyStore(config.keyStorePath, config.keyStorePassword).run { X509Utilities.loadKeyStore(config.keyStorePath, config.keyStorePassword).run {
assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_CA_PRIVATE_KEY)) assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_CA_PRIVATE_KEY))
@ -73,7 +74,7 @@ class CertificateSignerTest {
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA_PRIVATE_KEY)) assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA_PRIVATE_KEY))
} }
assertEquals(id, Files.readAllLines(config.certificatesPath / "certificate-request-id.txt").first()) assertEquals(id, (config.certificatesPath / "certificate-request-id.txt").readLines { it.findFirst().get() })
} }
} }

View File

@ -3,7 +3,10 @@ package com.r3corda.testing.node
import com.google.common.jimfs.Configuration.unix import com.google.common.jimfs.Configuration.unix
import com.google.common.jimfs.Jimfs import com.google.common.jimfs.Jimfs
import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.Futures
import com.r3corda.core.createDirectories
import com.r3corda.core.createDirectory
import com.r3corda.core.crypto.Party import com.r3corda.core.crypto.Party
import com.r3corda.core.div
import com.r3corda.core.messaging.SingleMessageRecipient import com.r3corda.core.messaging.SingleMessageRecipient
import com.r3corda.core.node.PhysicalLocation import com.r3corda.core.node.PhysicalLocation
import com.r3corda.core.node.services.KeyManagementService import com.r3corda.core.node.services.KeyManagementService
@ -13,11 +16,9 @@ import com.r3corda.core.random63BitValue
import com.r3corda.core.utilities.DUMMY_NOTARY_KEY import com.r3corda.core.utilities.DUMMY_NOTARY_KEY
import com.r3corda.core.utilities.loggerFor import com.r3corda.core.utilities.loggerFor
import com.r3corda.node.internal.AbstractNode import com.r3corda.node.internal.AbstractNode
import com.r3corda.node.internal.CordaRPCOpsImpl
import com.r3corda.node.services.api.MessagingServiceInternal import com.r3corda.node.services.api.MessagingServiceInternal
import com.r3corda.node.services.config.NodeConfiguration import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.keys.E2ETestKeyManagementService import com.r3corda.node.services.keys.E2ETestKeyManagementService
import com.r3corda.node.services.messaging.CordaRPCOps
import com.r3corda.node.services.messaging.RPCOps import com.r3corda.node.services.messaging.RPCOps
import com.r3corda.node.services.network.InMemoryNetworkMapService import com.r3corda.node.services.network.InMemoryNetworkMapService
import com.r3corda.node.services.network.NetworkMapService import com.r3corda.node.services.network.NetworkMapService
@ -29,7 +30,6 @@ import com.r3corda.node.utilities.AffinityExecutor
import com.r3corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import com.r3corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
import org.slf4j.Logger import org.slf4j.Logger
import java.nio.file.FileSystem import java.nio.file.FileSystem
import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.security.KeyPair import java.security.KeyPair
import java.util.* import java.util.*
@ -66,7 +66,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
val nodes: List<MockNode> = _nodes val nodes: List<MockNode> = _nodes
init { init {
Files.createDirectory(filesystem.getPath("/nodes")) filesystem.getPath("/nodes").createDirectory()
} }
/** Allows customisation of how nodes are created. */ /** Allows customisation of how nodes are created. */
@ -182,7 +182,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
val path = filesystem.getPath("/nodes/$id") val path = filesystem.getPath("/nodes/$id")
if (newNode) if (newNode)
Files.createDirectories(path.resolve("attachments")) (path / "attachments").createDirectories()
// TODO: create a base class that provides a default implementation // TODO: create a base class that provides a default implementation
val config = object : NodeConfiguration { val config = object : NodeConfiguration {