mirror of
https://github.com/corda/corda.git
synced 2024-12-25 15:41:10 +00:00
Added nice extension methods for Path, which are more readable than the static methods from Files
This commit is contained in:
parent
2bc8f8414d
commit
bd89da458b
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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 {
|
||||||
|
@ -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(
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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() })
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user