mirror of
https://github.com/corda/corda.git
synced 2025-06-20 08:03:53 +00:00
CORDA-2112 Update the uploader
field for Attachments loaded upon Node CorDapp startup (#4127)
* CORDA-2112 Update the `uploader` field upon Node startup for CorDapps where an Attachment is already in a nodes attachment store (from an untrusted source). * Removed incorrect assertion. * Fix broken unit test. * Reverted back throw FileAlreadyExistsException API semantics on public API. * De-duplicate DuplicateAttachmentException * Revert original Exception handling logic.
This commit is contained in:
@ -2,6 +2,7 @@ package net.corda.node.services.persistence
|
|||||||
|
|
||||||
import net.corda.core.node.services.AttachmentId
|
import net.corda.core.node.services.AttachmentId
|
||||||
import net.corda.core.node.services.AttachmentStorage
|
import net.corda.core.node.services.AttachmentStorage
|
||||||
|
import net.corda.nodeapi.exceptions.DuplicateAttachmentException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
interface AttachmentStorageInternal : AttachmentStorage {
|
interface AttachmentStorageInternal : AttachmentStorage {
|
||||||
@ -10,4 +11,9 @@ interface AttachmentStorageInternal : AttachmentStorage {
|
|||||||
* and is only for the node.
|
* and is only for the node.
|
||||||
*/
|
*/
|
||||||
fun privilegedImportAttachment(jar: InputStream, uploader: String, filename: String?): AttachmentId
|
fun privilegedImportAttachment(jar: InputStream, uploader: String, filename: String?): AttachmentId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to above but returns existing [AttachmentId] instead of throwing [DuplicateAttachmentException]
|
||||||
|
*/
|
||||||
|
fun privilegedImportOrGetAttachment(jar: InputStream, uploader: String, filename: String?): AttachmentId
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,11 @@ import com.google.common.hash.HashCode
|
|||||||
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 net.corda.core.ClientRelevantError
|
|
||||||
import net.corda.core.CordaRuntimeException
|
import net.corda.core.CordaRuntimeException
|
||||||
import net.corda.core.contracts.Attachment
|
import net.corda.core.contracts.Attachment
|
||||||
import net.corda.core.contracts.ContractAttachment
|
import net.corda.core.contracts.ContractAttachment
|
||||||
import net.corda.core.contracts.ContractClassName
|
import net.corda.core.contracts.ContractClassName
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.isFulfilledBy
|
|
||||||
import net.corda.core.crypto.sha256
|
import net.corda.core.crypto.sha256
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.*
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
@ -286,6 +284,14 @@ class NodeAttachmentService(
|
|||||||
return import(jar, uploader, filename)
|
return import(jar, uploader, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun privilegedImportOrGetAttachment(jar: InputStream, uploader: String, filename: String?): AttachmentId {
|
||||||
|
return try {
|
||||||
|
import(jar, uploader, filename)
|
||||||
|
} catch (faee: java.nio.file.FileAlreadyExistsException) {
|
||||||
|
AttachmentId.parse(faee.message!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun hasAttachment(attachmentId: AttachmentId): Boolean = database.transaction {
|
override fun hasAttachment(attachmentId: AttachmentId): Boolean = database.transaction {
|
||||||
currentDBSession().find(NodeAttachmentService.DBAttachment::class.java, attachmentId.toString()) != null
|
currentDBSession().find(NodeAttachmentService.DBAttachment::class.java, attachmentId.toString()) != null
|
||||||
}
|
}
|
||||||
@ -306,9 +312,7 @@ class NodeAttachmentService(
|
|||||||
val id = bytes.sha256()
|
val id = bytes.sha256()
|
||||||
if (!hasAttachment(id)) {
|
if (!hasAttachment(id)) {
|
||||||
checkIsAValidJAR(bytes.inputStream())
|
checkIsAValidJAR(bytes.inputStream())
|
||||||
|
|
||||||
val jarSigners = getSigners(bytes)
|
val jarSigners = getSigners(bytes)
|
||||||
|
|
||||||
val session = currentDBSession()
|
val session = currentDBSession()
|
||||||
val attachment = NodeAttachmentService.DBAttachment(
|
val attachment = NodeAttachmentService.DBAttachment(
|
||||||
attId = id.toString(),
|
attId = id.toString(),
|
||||||
@ -318,14 +322,24 @@ class NodeAttachmentService(
|
|||||||
contractClassNames = contractClassNames,
|
contractClassNames = contractClassNames,
|
||||||
signers = jarSigners
|
signers = jarSigners
|
||||||
)
|
)
|
||||||
|
|
||||||
session.save(attachment)
|
session.save(attachment)
|
||||||
attachmentCount.inc()
|
attachmentCount.inc()
|
||||||
log.info("Stored new attachment $id")
|
log.info("Stored new attachment $id")
|
||||||
id
|
return@withContractsInJar id
|
||||||
} else {
|
|
||||||
throw DuplicateAttachmentException(id.toString())
|
|
||||||
}
|
}
|
||||||
|
if (isUploaderTrusted(uploader)) {
|
||||||
|
val session = currentDBSession()
|
||||||
|
val attachment = session.get(NodeAttachmentService.DBAttachment::class.java, id.toString())
|
||||||
|
// update the `upLoader` field (as the existing attachment may have been resolved from a peer)
|
||||||
|
if (attachment.uploader != uploader) {
|
||||||
|
attachment.uploader = uploader
|
||||||
|
session.saveOrUpdate(attachment)
|
||||||
|
log.info("Updated attachment $id with uploader $uploader")
|
||||||
|
attachmentCache.invalidate(id)
|
||||||
|
attachmentContentCache.invalidate(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw DuplicateAttachmentException(id.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import com.nhaarman.mockito_kotlin.whenever
|
|||||||
import net.corda.core.JarSignatureTestUtils.createJar
|
import net.corda.core.JarSignatureTestUtils.createJar
|
||||||
import net.corda.core.JarSignatureTestUtils.generateKey
|
import net.corda.core.JarSignatureTestUtils.generateKey
|
||||||
import net.corda.core.JarSignatureTestUtils.signJar
|
import net.corda.core.JarSignatureTestUtils.signJar
|
||||||
|
import net.corda.core.contracts.ContractAttachment
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.sha256
|
import net.corda.core.crypto.sha256
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
@ -47,6 +48,7 @@ import javax.tools.StandardLocation
|
|||||||
import javax.tools.ToolProvider
|
import javax.tools.ToolProvider
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
|
import kotlin.test.assertNotEquals
|
||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
|
|
||||||
@ -123,6 +125,30 @@ class NodeAttachmentServiceTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `insert contract attachment as an untrusted uploader and then as trusted CorDapp uploader`() {
|
||||||
|
val contractJarName = makeTestContractJar("com.example.MyContract")
|
||||||
|
val testJar = dir.resolve(contractJarName)
|
||||||
|
val expectedHash = testJar.readAll().sha256()
|
||||||
|
|
||||||
|
// PRIVILEGED_UPLOADERS = listOf(DEPLOYED_CORDAPP_UPLOADER, RPC_UPLOADER, P2P_UPLOADER, UNKNOWN_UPLOADER)
|
||||||
|
// TRUSTED_UPLOADERS = listOf(DEPLOYED_CORDAPP_UPLOADER, RPC_UPLOADER)
|
||||||
|
|
||||||
|
database.transaction {
|
||||||
|
val id = testJar.read { storage.privilegedImportOrGetAttachment(it, P2P_UPLOADER, null) }
|
||||||
|
assertEquals(expectedHash, id)
|
||||||
|
val attachment1 = storage.openAttachment(expectedHash)
|
||||||
|
|
||||||
|
val id2 = testJar.read { storage.privilegedImportOrGetAttachment(it, DEPLOYED_CORDAPP_UPLOADER, null) }
|
||||||
|
assertEquals(expectedHash, id2)
|
||||||
|
val attachment2 = storage.openAttachment(expectedHash)
|
||||||
|
|
||||||
|
assertNotEquals(attachment1, attachment2)
|
||||||
|
assertEquals(P2P_UPLOADER, (attachment1 as ContractAttachment).uploader)
|
||||||
|
assertEquals(DEPLOYED_CORDAPP_UPLOADER, (attachment2 as ContractAttachment).uploader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `missing is not cached`() {
|
fun `missing is not cached`() {
|
||||||
val (testJar, expectedHash) = makeTestJar()
|
val (testJar, expectedHash) = makeTestJar()
|
||||||
|
Reference in New Issue
Block a user