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:
josecoll 2018-10-30 15:44:29 +00:00 committed by GitHub
parent 13815d252e
commit e064800173
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 8 deletions

View File

@ -2,6 +2,7 @@ package net.corda.node.services.persistence
import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.AttachmentStorage
import net.corda.nodeapi.exceptions.DuplicateAttachmentException
import java.io.InputStream
interface AttachmentStorageInternal : AttachmentStorage {
@ -10,4 +11,9 @@ interface AttachmentStorageInternal : AttachmentStorage {
* and is only for the node.
*/
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
}

View File

@ -6,13 +6,11 @@ import com.google.common.hash.HashCode
import com.google.common.hash.Hashing
import com.google.common.hash.HashingInputStream
import com.google.common.io.CountingInputStream
import net.corda.core.ClientRelevantError
import net.corda.core.CordaRuntimeException
import net.corda.core.contracts.Attachment
import net.corda.core.contracts.ContractAttachment
import net.corda.core.contracts.ContractClassName
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.isFulfilledBy
import net.corda.core.crypto.sha256
import net.corda.core.internal.*
import net.corda.core.node.ServicesForResolution
@ -286,6 +284,14 @@ class NodeAttachmentService(
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 {
currentDBSession().find(NodeAttachmentService.DBAttachment::class.java, attachmentId.toString()) != null
}
@ -306,9 +312,7 @@ class NodeAttachmentService(
val id = bytes.sha256()
if (!hasAttachment(id)) {
checkIsAValidJAR(bytes.inputStream())
val jarSigners = getSigners(bytes)
val session = currentDBSession()
val attachment = NodeAttachmentService.DBAttachment(
attId = id.toString(),
@ -318,14 +322,24 @@ class NodeAttachmentService(
contractClassNames = contractClassNames,
signers = jarSigners
)
session.save(attachment)
attachmentCount.inc()
log.info("Stored new attachment $id")
id
} else {
throw DuplicateAttachmentException(id.toString())
return@withContractsInJar id
}
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())
}
}
}

View File

@ -9,6 +9,7 @@ import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.JarSignatureTestUtils.createJar
import net.corda.core.JarSignatureTestUtils.generateKey
import net.corda.core.JarSignatureTestUtils.signJar
import net.corda.core.contracts.ContractAttachment
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
import net.corda.core.flows.FlowLogic
@ -47,6 +48,7 @@ import javax.tools.StandardLocation
import javax.tools.ToolProvider
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNotEquals
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
fun `missing is not cached`() {
val (testJar, expectedHash) = makeTestJar()