CORDA-786 - Add whitelist testing for external custom serializers

Update Docs
This commit is contained in:
Katelyn Baker
2017-11-24 12:04:17 +00:00
parent f931b74aec
commit 6881350493
2 changed files with 99 additions and 28 deletions

View File

@ -1,19 +1,19 @@
Pluggable Serializers for CorDapps Pluggable Serializers for CorDapps
================================== ==================================
To be serializable by Corda Java classes must be compiled with the -parameter switch to enable matching of it's properties
To be serializable by Corda Java classes must be compiled with the -parameter switch to enable mathcing of ass property to constructor parameters. This is important because Corda's internal AMQP serialization scheme will only constuct
to constructor parameter. However, when this isn't possible CorDapps can provide custom proxy serialiszers that Corda objects using their constructors. However, when recompilation isn't possible, or classes are built in such a way that
can use to move from types it cannot serialiser to an interim represtnation that it can with the transformation to and they cannot be easily modified for simple serailization, CorDapps can provide custom proxy serializers that Corda
from this proxy object being handled by the supplied serialiser. can use to move from types it cannot serializer to an interim representation that it can with the transformation to and
from this proxy object being handled by the supplied serializer.
Serializer Location Serializer Location
------------------- -------------------
Custom serializers should be placed in the plugins directory fo a CorDapp or a sub directory (placing it in a sub Custom serializers should be placed in the plugins directory of a CorDapp or a sub directory thereof. These
directory however does require that directory be added to the list of locations scanned within the jar) classes will be scanned and loaded by the CorDapp loading process.
Writing a Custom Serializer Writing a Custom Serializer
-------------------------- ---------------------------
Serializers must Serializers must
* Inherit from net.corda.core.serialization.SerializationCustomSerializer * Inherit from net.corda.core.serialization.SerializationCustomSerializer
* Be annotated with the @CordaCustomSerializer annotation * Be annotated with the @CordaCustomSerializer annotation
@ -24,7 +24,6 @@ Serializers inheriting from SerializationCustomSerializer have to implement two
Example Example
------- -------
Consider this example class Consider this example class
.. sourcecode:: java .. sourcecode:: java
@ -43,7 +42,10 @@ Consider this example class
public int getB() { return b; } public int getB() { return b; }
} }
This would require a serialiser as follows Without a custom serializer we cannot serialise this class as there is no public constructor that facilitates the
initialisation of al of its's properties.
To be serializable by Corda this would require a custom serializer as follows
.. sourcecode:: kotlin .. sourcecode:: kotlin
@CordaCustomSerializer @CordaCustomSerializer
@ -64,6 +66,9 @@ This would require a serialiser as follows
override val ptype: Type get() = Proxy::class.java override val ptype: Type get() = Proxy::class.java
} }
Whitelisting
------------
By writing a custom serializer for a class it has the effect of adding that class to the whitelist, meaning such
classes don't need explicitly adding to the CorDapp's whitelist

View File

@ -3,7 +3,10 @@ package net.corda.nodeapi.internal.serialization.amqp
import org.junit.Test import org.junit.Test
import net.corda.core.serialization.CordaCustomSerializer import net.corda.core.serialization.CordaCustomSerializer
import net.corda.core.serialization.CordaCustomSerializerProxy import net.corda.core.serialization.CordaCustomSerializerProxy
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.SerializationCustomSerializer import net.corda.core.serialization.SerializationCustomSerializer
import org.assertj.core.api.Assertions
import java.io.NotSerializableException
import java.lang.reflect.Type import java.lang.reflect.Type
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -18,31 +21,25 @@ class CorDappSerializerTests {
override val type: Type get() = NeedsProxy::class.java override val type: Type get() = NeedsProxy::class.java
override val ptype: Type get() = Proxy::class.java override val ptype: Type get() = Proxy::class.java
override fun fromProxy(proxy: Any) : Any { override fun fromProxy(proxy: Any) : Any = NeedsProxy((proxy as Proxy).proxy_a_)
println ("NeedsProxyProxySerialiser - fromProxy") override fun toProxy(obj: Any) : Any = Proxy((obj as NeedsProxy).a)
return NeedsProxy((proxy as Proxy).proxy_a_)
}
override fun toProxy(obj: Any) : Any {
println ("NeedsProxyProxySerialiser - to Proxy")
return Proxy((obj as NeedsProxy).a)
}
} }
// Standard proxy serialiser used internally, here for comparison purposes // Standard proxy serializer used internally, here for comparison purposes
class InternalProxySerialiser(factory: SerializerFactory) : class InternalProxySerializer(factory: SerializerFactory) :
CustomSerializer.Proxy<NeedsProxy, InternalProxySerialiser.Proxy> ( CustomSerializer.Proxy<NeedsProxy, InternalProxySerializer.Proxy> (
NeedsProxy::class.java, NeedsProxy::class.java,
InternalProxySerialiser.Proxy::class.java, InternalProxySerializer.Proxy::class.java,
factory) { factory) {
data class Proxy(val proxy_a_: String) data class Proxy(val proxy_a_: String)
override fun toProxy(obj: NeedsProxy): Proxy { override fun toProxy(obj: NeedsProxy): Proxy {
println ("InternalProxySerialiser - toProxy") println ("InternalProxySerializer - toProxy")
return Proxy(obj.a) return Proxy(obj.a)
} }
override fun fromProxy(proxy: Proxy): NeedsProxy { override fun fromProxy(proxy: Proxy): NeedsProxy {
println ("InternalProxySerialiser - fromProxy") println ("InternalProxySerializer - fromProxy")
return NeedsProxy(proxy.proxy_a_) return NeedsProxy(proxy.proxy_a_)
} }
} }
@ -56,7 +53,7 @@ class CorDappSerializerTests {
val msg = "help" val msg = "help"
proxyFactory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), proxyFactory)) proxyFactory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), proxyFactory))
internalProxyFactory.register (InternalProxySerialiser(internalProxyFactory)) internalProxyFactory.register (InternalProxySerializer(internalProxyFactory))
val needsProxy = NeedsProxy(msg) val needsProxy = NeedsProxy(msg)
@ -89,4 +86,73 @@ class CorDappSerializerTests {
assertEquals(tv1, objFromDefault.obj.a) assertEquals(tv1, objFromDefault.obj.a)
assertEquals(tv2, objFromDefault.obj.b.a) assertEquals(tv2, objFromDefault.obj.b.a)
} }
@Test
fun testWithWhitelistNotAllowed() {
data class A (val a: Int, val b: NeedsProxy)
class WL : ClassWhitelist {
private val allowedClasses = emptySet<String>()
override fun hasListed(type: Class<*>): Boolean = type.name in allowedClasses
}
val factory = SerializerFactory(WL(), ClassLoader.getSystemClassLoader())
factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
val tv1 = 100
val tv2 = "pants schmants"
Assertions.assertThatThrownBy {
SerializationOutput(factory).serialize(A(tv1, NeedsProxy(tv2)))
}.isInstanceOf(NotSerializableException::class.java)
}
@Test
fun testWithWhitelistAllowed() {
data class A (val a: Int, val b: NeedsProxy)
class WL : ClassWhitelist {
private val allowedClasses = hashSetOf(
A::class.java.name,
NeedsProxy::class.java.name)
override fun hasListed(type: Class<*>): Boolean = type.name in allowedClasses
}
val factory = SerializerFactory(WL(), ClassLoader.getSystemClassLoader())
factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
val tv1 = 100
val tv2 = "pants schmants"
val obj = DeserializationInput(factory).deserialize(
SerializationOutput(factory).serialize(A(tv1, NeedsProxy(tv2))))
assertEquals(tv1, obj.a)
assertEquals(tv2, obj.b.a)
}
// The custom type not being whitelisted won't matter here because the act of adding a
// custom serializer bypasses the whitelist
@Test
fun testWithWhitelistAllowedOuterOnly() {
data class A (val a: Int, val b: NeedsProxy)
class WL : ClassWhitelist {
// explicitly don't add NeedsProxy
private val allowedClasses = hashSetOf(A::class.java.name)
override fun hasListed(type: Class<*>): Boolean = type.name in allowedClasses
}
val factory = SerializerFactory(WL(), ClassLoader.getSystemClassLoader())
factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
val tv1 = 100
val tv2 = "pants schmants"
val obj = DeserializationInput(factory).deserialize(
SerializationOutput(factory).serialize(A(tv1, NeedsProxy(tv2))))
assertEquals(tv1, obj.a)
assertEquals(tv2, obj.b.a)
}
} }