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-reflect:$kotlin_version"
compile "org.jetbrains.kotlinx:kotlinx-support-jdk8:0.2"
compile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
// 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.SettableFuture
import com.r3corda.core.crypto.newSecureRandom
import kotlinx.support.jdk7.use
import org.slf4j.Logger
import rx.Observable
import rx.subjects.UnicastSubject
@ -15,16 +16,16 @@ import java.io.BufferedInputStream
import java.io.InputStream
import java.io.OutputStream
import java.math.BigDecimal
import java.nio.file.Files
import java.nio.file.LinkOption
import java.nio.file.OpenOption
import java.nio.file.Path
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.*
import java.nio.file.attribute.FileAttribute
import java.time.Duration
import java.time.temporal.Temporal
import java.util.concurrent.ExecutionException
import java.util.concurrent.Executor
import java.util.concurrent.locks.ReentrantLock
import java.util.stream.Stream
import java.util.zip.ZipInputStream
import kotlin.concurrent.withLock
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. */
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.exists(vararg options: LinkOption): Boolean = Files.exists(this, *options)
inline fun <R> Path.use(block: (InputStream) -> R): R = Files.newInputStream(this).use(block)
inline fun Path.write(createDirs: Boolean = false, vararg options: OpenOption, block: (OutputStream) -> Unit) {
fun Path.moveTo(target: Path, vararg options: CopyOption): Path = Files.move(this, target, *options)
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) {
normalize().parent?.createDirectories()
}
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
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) {
val normalisedToPath = toPath.normalize()
if (!Files.exists(normalisedToPath))
Files.createDirectories(normalisedToPath)
normalisedToPath.createDirectories()
ZipInputStream(BufferedInputStream(Files.newInputStream(zipPath))).use { zip ->
zipPath.read {
val zip = ZipInputStream(BufferedInputStream(it))
while (true) {
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.
if (!outPath.normalize().startsWith(normalisedToPath))
throw IllegalStateException("ZIP contained a path that resolved incorrectly: ${e.name}")
if (e.isDirectory) {
Files.createDirectories(outPath)
outPath.createDirectories()
continue
}
Files.newOutputStream(outPath).use { out ->
outPath.write { out ->
ByteStreams.copy(zip, out)
}
zip.closeEntry()

View File

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

View File

@ -1,5 +1,6 @@
package com.r3corda.core.crypto
import com.r3corda.core.div
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x500.style.BCStyle
import org.bouncycastle.asn1.x509.GeneralName
@ -11,6 +12,7 @@ import java.io.DataOutputStream
import java.io.IOException
import java.net.InetAddress
import java.net.InetSocketAddress
import java.nio.file.Path
import java.security.PrivateKey
import java.security.SecureRandom
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
}
@Test
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")
X509Utilities.saveCertificateAsPEMFile(caCertAndKey.certificate, tmpCertificateFile)
val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile)
assertEquals(caCertAndKey.certificate, readCertificate)
}
@Test
fun `create valid server certificate chain`() {
val caCertAndKey = X509Utilities.createSelfSignedCACert("Test CA Cert")
@ -84,15 +83,11 @@ class X509UtilitiesTest {
@Test
fun `create full CA keystore`() {
val tmpKeyStore = tempFolder.root.toPath().resolve("keystore.jks")
val tmpTrustStore = tempFolder.root.toPath().resolve("truststore.jks")
val tmpKeyStore = tempFile("keystore.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
X509Utilities.createCAKeyStoreAndTrustStore(tmpKeyStore,
"keystorepass",
"keypass",
tmpTrustStore,
"trustpass")
X509Utilities.createCAKeyStoreAndTrustStore(tmpKeyStore, "keystorepass", "keypass", tmpTrustStore, "trustpass")
// Load back generated root CA Cert and private key from keystore and check against copy in truststore
val keyStore = X509Utilities.loadKeyStore(tmpKeyStore, "keystorepass")
@ -132,12 +127,11 @@ class X509UtilitiesTest {
assertTrue { intermediateVerifier.verify(intermediateSignature) }
}
@Test
fun `create server certificate in keystore for SSL`() {
val tmpCAKeyStore = tempFolder.root.toPath().resolve("keystore.jks")
val tmpTrustStore = tempFolder.root.toPath().resolve("truststore.jks")
val tmpServerKeyStore = tempFolder.root.toPath().resolve("serverkeystore.jks")
val tmpCAKeyStore = tempFile("keystore.jks")
val tmpTrustStore = tempFile("truststore.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
X509Utilities.createCAKeyStoreAndTrustStore(tmpCAKeyStore,
@ -177,9 +171,9 @@ class X509UtilitiesTest {
@Test
fun `create server cert and use in SSL socket`() {
val tmpCAKeyStore = tempFolder.root.toPath().resolve("keystore.jks")
val tmpTrustStore = tempFolder.root.toPath().resolve("truststore.jks")
val tmpServerKeyStore = tempFolder.root.toPath().resolve("serverkeystore.jks")
val tmpCAKeyStore = tempFile("keystore.jks")
val tmpTrustStore = tempFile("truststore.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
val caKeyStore = X509Utilities.createCAKeyStoreAndTrustStore(tmpCAKeyStore,
@ -218,7 +212,6 @@ class X509UtilitiesTest {
serverSocket.sslParameters = serverParams
serverSocket.useClientMode = false
val clientSocket = clientSocketFactory.createSocket() as SSLSocket
val clientParams = SSLParameters(arrayOf("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
@ -285,4 +278,6 @@ class X509UtilitiesTest {
serverSocket.close()
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-reflect:$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"

View File

@ -4,6 +4,7 @@ import com.codahale.metrics.MetricRegistry
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors
import com.google.common.util.concurrent.SettableFuture
import com.r3corda.core.*
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.X509Utilities
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.protocols.ProtocolLogic
import com.r3corda.core.protocols.ProtocolLogicRefFactory
import com.r3corda.core.seconds
import com.r3corda.core.serialization.SingletonSerializeAsToken
import com.r3corda.core.serialization.deserialize
import com.r3corda.core.serialization.serialize
@ -47,7 +47,6 @@ import com.r3corda.protocols.sendRequest
import org.jetbrains.exposed.sql.Database
import org.slf4j.Logger
import java.nio.file.FileAlreadyExistsException
import java.nio.file.Files
import java.nio.file.Path
import java.security.KeyPair
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
// 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.
val privKeyFile = dir.resolve(privateKeyFileName)
val pubIdentityFile = dir.resolve(publicKeyFileName)
val privKeyFile = dir / privateKeyFileName
val pubIdentityFile = dir / publicKeyFileName
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!")
val keyPair: KeyPair = generateKeyPair()
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.
// This is just a sanity check. It shouldn't fail unless the admin has fiddled with the files and messed
// things up for us.
val myIdentity = Files.readAllBytes(pubIdentityFile).deserialize<Party>()
val myIdentity = pubIdentityFile.readAll().deserialize<Party>()
if (myIdentity.name != identityName)
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.
val keyPair = Files.readAllBytes(privKeyFile).deserialize<KeyPair>()
val keyPair = privKeyFile.readAll().deserialize<KeyPair>()
Pair(myIdentity, keyPair)
}
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 fun makeAttachmentStorage(dir: Path): NodeAttachmentService {
val attachmentsDir = dir.resolve("attachments")
val attachmentsDir = dir / "attachments"
try {
Files.createDirectory(attachmentsDir)
attachmentsDir.createDirectory()
} catch (e: FileAlreadyExistsException) {
}
return NodeAttachmentService(attachmentsDir, services.monitoringService.metrics)
}
protected fun createNodeDir() {
if (!Files.exists(configuration.basedir)) {
Files.createDirectories(configuration.basedir)
}
configuration.basedir.createDirectories()
}
}

View File

@ -1,6 +1,7 @@
package com.r3corda.node.internal
import com.codahale.metrics.JmxReporter
import com.r3corda.core.div
import com.r3corda.core.messaging.SingleMessageRecipient
import com.r3corda.core.node.ServiceHub
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
// 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.
val pidPath = configuration.basedir.resolve("process-id")
val pidPath = configuration.basedir / "process-id"
val file = pidPath.toFile()
if (!file.exists()) {
file.createNewFile()

View File

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

View File

@ -1,7 +1,10 @@
package com.r3corda.node.services.config
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.div
import com.r3corda.core.exists
import com.r3corda.core.utilities.loggerFor
import com.typesafe.config.Config
@ -29,7 +32,7 @@ object ConfigHelper {
val defaultConfig = ConfigFactory.parseResources("reference.conf", ConfigParseOptions.defaults().setAllowMissing(false))
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 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.
*/
fun NodeSSLConfiguration.configureWithDevSSLCertificate() {
Files.createDirectories(certificatesPath)
certificatesPath.createDirectories()
if (!trustStorePath.exists()) {
Files.copy(javaClass.classLoader.getResourceAsStream("com/r3corda/node/internal/certificates/cordatruststore.jks"),
trustStorePath)
javaClass.classLoader.getResourceAsStream("com/r3corda/node/internal/certificates/cordatruststore.jks").copyTo(trustStorePath)
}
if (!keyStorePath.exists()) {
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.SingleMessageRecipient
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.configureWithDevSSLCertificate
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.
*/
fun checkStorePasswords() {
config.keyStorePath.use {
config.keyStorePath.read {
KeyStore.getInstance("JKS").load(it, config.keyStorePassword.toCharArray())
}
config.trustStorePath.use {
config.trustStorePath.read {
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.HashingInputStream
import com.google.common.io.CountingInputStream
import com.r3corda.core.*
import com.r3corda.core.contracts.Attachment
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.extractZipFile
import com.r3corda.core.node.services.AttachmentStorage
import com.r3corda.core.utilities.loggerFor
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.
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
@ -45,7 +45,7 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
@Volatile var automaticallyExtractAttachments = false
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() {
@ -66,7 +66,7 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
private val counter: CountingInputStream = CountingInputStream(input),
private val stream: HashingInputStream = HashingInputStream(Hashing.sha256(), counter)) : FilterInputStream(stream) {
private val expectedSize = Files.size(filePath)
private val expectedSize = filePath.size
override fun close() {
super.close()
@ -94,8 +94,8 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
}
override fun openAttachment(id: SecureHash): Attachment? {
val path = storePath.resolve(id.toString())
if (!Files.exists(path)) return null
val path = storePath / id.toString()
if (!path.exists()) return null
return AttachmentImpl(id, path, checkAttachmentsOnLoad)
}
@ -103,28 +103,28 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
override fun importAttachment(jar: InputStream): SecureHash {
require(jar !is JarInputStream)
val hs = HashingInputStream(Hashing.sha256(), jar)
val tmp = storePath.resolve("tmp.${UUID.randomUUID()}")
Files.copy(hs, tmp)
val tmp = storePath / "tmp.${UUID.randomUUID()}"
hs.copyTo(tmp)
checkIsAValidJAR(tmp)
val id = SecureHash.SHA256(hs.hash().asBytes())
val finalPath = storePath.resolve(id.toString())
val finalPath = storePath / id.toString()
try {
// 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.
if (!Files.exists(finalPath)) {
if (!finalPath.exists()) {
log.info("Stored new attachment $id")
attachmentCount.inc()
} else {
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 {
Files.deleteIfExists(tmp)
tmp.deleteIfExists()
}
if (automaticallyExtractAttachments) {
val extractTo = storePath.resolve("$id.jar")
val extractTo = storePath / "$id.jar"
try {
Files.createDirectory(extractTo)
extractTo.createDirectory()
extractZipFile(finalPath, extractTo)
} catch(e: FileAlreadyExistsException) {
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) {
// 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) {
val cursor = stream.nextJarEntry ?: break
val cursor = jar.nextJarEntry ?: break
val entryPath = Paths.get(cursor.name)
// Security check to stop zips trying to escape their rightful place.
if (entryPath.isAbsolute || entryPath.normalize() != entryPath || '\\' in cursor.name)

View File

@ -1,13 +1,12 @@
package com.r3corda.node.utilities.certsigning
import com.r3corda.core.*
import com.r3corda.core.crypto.X509Utilities
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_ROOT_CA
import com.r3corda.core.crypto.X509Utilities.addOrReplaceCertificate
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.node.services.config.ConfigHelper
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 joptsimple.OptionParser
import java.net.URL
import java.nio.file.Files
import java.nio.file.Paths
import java.security.KeyPair
import java.security.cert.Certificate
@ -34,7 +32,7 @@ class CertificateSigner(val config: NodeConfiguration, val certService: Certific
}
fun buildKeyStore() {
Files.createDirectories(config.certificatesPath)
config.certificatesPath.createDirectories()
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 {
val requestIdStore = config.certificatesPath / "certificate-request-id.txt"
// 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)
// Post request to signing server via http.
val requestId = certService.submitRequest(request)
// Persists request ID to file in case of node shutdown.
Files.write(requestIdStore, listOf(requestId), Charsets.UTF_8)
requestIdStore.writeLines(listOf(requestId))
requestId
} 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.messaging.SingleMessageRecipient
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.network.NetworkMapService
import com.r3corda.node.services.persistence.NodeAttachmentService
@ -18,9 +18,6 @@ import org.junit.Before
import org.junit.Test
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
import java.nio.file.Files
import java.nio.file.StandardOpenOption
import java.security.KeyPair
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry
@ -103,9 +100,7 @@ class AttachmentTests {
val id = n0.storage.attachments.importAttachment(ByteArrayInputStream(fakeAttachment()))
// Corrupt its store.
val writer = Files.newByteChannel(network.filesystem.getPath("/nodes/0/attachments/$id"), StandardOpenOption.WRITE)
writer.write(ByteBuffer.wrap(OpaqueBytes.of(99, 99, 99, 99).bits))
writer.close()
network.filesystem.getPath("/nodes/0/attachments/$id").write { it.write(byteArrayOf(99, 99, 99, 99)) }
// Get n1 to fetch the attachment. Should receive corrupted bytes.
network.runNetwork()

View File

@ -4,12 +4,19 @@ import com.codahale.metrics.MetricRegistry
import com.google.common.jimfs.Configuration
import com.google.common.jimfs.Jimfs
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 org.junit.Before
import org.junit.Test
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.JarOutputStream
import kotlin.test.assertEquals
@ -28,10 +35,10 @@ class NodeAttachmentStorageTest {
@Test
fun `insert and retrieve`() {
val testJar = makeTestJar()
val expectedHash = SecureHash.sha256(Files.readAllBytes(testJar))
val expectedHash = testJar.readAll().sha256()
val storage = NodeAttachmentService(fs.getPath("/"), MetricRegistry())
val id = testJar.use { storage.importAttachment(it) }
val id = testJar.read { storage.importAttachment(it) }
assertEquals(expectedHash, id)
assertNull(storage.openAttachment(SecureHash.randomSHA256()))
@ -48,9 +55,9 @@ class NodeAttachmentStorageTest {
fun `duplicates not allowed`() {
val testJar = makeTestJar()
val storage = NodeAttachmentService(fs.getPath("/"), MetricRegistry())
testJar.use { storage.importAttachment(it) }
testJar.read { storage.importAttachment(it) }
assertFailsWith<FileAlreadyExistsException> {
testJar.use { storage.importAttachment(it) }
testJar.read { storage.importAttachment(it) }
}
}
@ -58,15 +65,15 @@ class NodeAttachmentStorageTest {
fun `corrupt entry throws exception`() {
val testJar = makeTestJar()
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.
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> {
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.
storage.openAttachment(id)!!.openAsJAR().use {
@ -78,15 +85,16 @@ class NodeAttachmentStorageTest {
private var counter = 0
private fun makeTestJar(): Path {
counter++
val f = fs.getPath("$counter.jar")
JarOutputStream(Files.newOutputStream(f)).use {
it.putNextEntry(JarEntry("test1.txt"))
it.write("This is some useful content".toByteArray())
it.closeEntry()
it.putNextEntry(JarEntry("test2.txt"))
it.write("Some more useful content".toByteArray())
it.closeEntry()
val file = fs.getPath("$counter.jar")
file.write {
val jar = JarOutputStream(it)
jar.putNextEntry(JarEntry("test1.txt"))
jar.write("This is some useful content".toByteArray())
jar.closeEntry()
jar.putNextEntry(JarEntry("test2.txt"))
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.Jimfs
import com.r3corda.core.writeLines
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.After
import org.junit.Test
import java.nio.file.Files
class PropertiesFileRPCUserServiceTest {
@ -75,7 +75,7 @@ class PropertiesFileRPCUserServiceTest {
}
private fun loadWithContents(vararg lines: String): PropertiesFileRPCUserService {
Files.write(file, lines.asList())
file.writeLines(lines.asList())
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.X509Utilities
import com.r3corda.core.div
import com.r3corda.core.exists
import com.r3corda.core.readLines
import com.r3corda.node.services.config.NodeConfiguration
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import java.nio.file.Files
import java.nio.file.Path
import kotlin.test.assertEquals
import kotlin.test.assertFalse
@ -47,13 +48,13 @@ class CertificateSignerTest {
override val trustStorePassword: String = "trustpass"
}
assertFalse(Files.exists(config.keyStorePath))
assertFalse(Files.exists(config.trustStorePath))
assertFalse(config.keyStorePath.exists())
assertFalse(config.trustStorePath.exists())
CertificateSigner(config, certService).buildKeyStore()
assertTrue(Files.exists(config.keyStorePath))
assertTrue(Files.exists(config.trustStorePath))
assertTrue(config.keyStorePath.exists())
assertTrue(config.trustStorePath.exists())
X509Utilities.loadKeyStore(config.keyStorePath, config.keyStorePassword).run {
assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_CA_PRIVATE_KEY))
@ -73,7 +74,7 @@ class CertificateSignerTest {
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.Jimfs
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.div
import com.r3corda.core.messaging.SingleMessageRecipient
import com.r3corda.core.node.PhysicalLocation
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.loggerFor
import com.r3corda.node.internal.AbstractNode
import com.r3corda.node.internal.CordaRPCOpsImpl
import com.r3corda.node.services.api.MessagingServiceInternal
import com.r3corda.node.services.config.NodeConfiguration
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.network.InMemoryNetworkMapService
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 org.slf4j.Logger
import java.nio.file.FileSystem
import java.nio.file.Files
import java.nio.file.Path
import java.security.KeyPair
import java.util.*
@ -66,7 +66,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
val nodes: List<MockNode> = _nodes
init {
Files.createDirectory(filesystem.getPath("/nodes"))
filesystem.getPath("/nodes").createDirectory()
}
/** Allows customisation of how nodes are created. */
@ -182,7 +182,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
val path = filesystem.getPath("/nodes/$id")
if (newNode)
Files.createDirectories(path.resolve("attachments"))
(path / "attachments").createDirectories()
// TODO: create a base class that provides a default implementation
val config = object : NodeConfiguration {