mirror of
https://github.com/corda/corda.git
synced 2025-06-22 09:08:49 +00:00
CORDA-786 - Add whitelist testing for external custom serializers
Update Docs
This commit is contained in:
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user