mirror of
https://github.com/corda/corda.git
synced 2025-02-21 01:42:24 +00:00
CORDA-786 - Add whitelist testing for external custom serializers
Update Docs
This commit is contained in:
parent
f931b74aec
commit
6881350493
@ -1,30 +1,29 @@
|
||||
Pluggable Serializers for CorDapps
|
||||
==================================
|
||||
|
||||
To be serializable by Corda Java classes must be compiled with the -parameter switch to enable mathcing of ass property
|
||||
to constructor parameter. However, when this isn't possible CorDapps can provide custom proxy serialiszers that Corda
|
||||
can use to move from types it cannot serialiser to an interim represtnation that it can with the transformation to and
|
||||
from this proxy object being handled by the supplied serialiser.
|
||||
To be serializable by Corda Java classes must be compiled with the -parameter switch to enable matching of it's properties
|
||||
to constructor parameters. This is important because Corda's internal AMQP serialization scheme will only constuct
|
||||
objects using their constructors. However, when recompilation isn't possible, or classes are built in such a way that
|
||||
they cannot be easily modified for simple serailization, CorDapps can provide custom proxy serializers that Corda
|
||||
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
|
||||
-------------------
|
||||
Custom serializers should be placed in the plugins directory fo a CorDapp or a sub directory (placing it in a sub
|
||||
directory however does require that directory be added to the list of locations scanned within the jar)
|
||||
Custom serializers should be placed in the plugins directory of a CorDapp or a sub directory thereof. These
|
||||
classes will be scanned and loaded by the CorDapp loading process.
|
||||
|
||||
Writing a Custom Serializer
|
||||
--------------------------
|
||||
|
||||
---------------------------
|
||||
Serializers must
|
||||
* Inherit from net.corda.core.serialization.SerializationCustomSerializer
|
||||
* Be annotated with the @CordaCustomSerializer annotation
|
||||
* Provide a proxy class to transform the objectto and from
|
||||
* Provide a proxy class to transform the object to and from
|
||||
* Have that proxy class annotated with the @CordaCustomSerializerProxy annotation
|
||||
|
||||
Serializers inheriting from SerializationCustomSerializer have to implement two methods and two types
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
Consider this example class
|
||||
|
||||
.. sourcecode:: java
|
||||
@ -43,7 +42,10 @@ Consider this example class
|
||||
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
|
||||
@CordaCustomSerializer
|
||||
@ -64,6 +66,9 @@ This would require a serialiser as follows
|
||||
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 net.corda.core.serialization.CordaCustomSerializer
|
||||
import net.corda.core.serialization.CordaCustomSerializerProxy
|
||||
import net.corda.core.serialization.ClassWhitelist
|
||||
import net.corda.core.serialization.SerializationCustomSerializer
|
||||
import org.assertj.core.api.Assertions
|
||||
import java.io.NotSerializableException
|
||||
import java.lang.reflect.Type
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@ -18,31 +21,25 @@ class CorDappSerializerTests {
|
||||
override val type: Type get() = NeedsProxy::class.java
|
||||
override val ptype: Type get() = Proxy::class.java
|
||||
|
||||
override fun fromProxy(proxy: Any) : Any {
|
||||
println ("NeedsProxyProxySerialiser - fromProxy")
|
||||
return NeedsProxy((proxy as Proxy).proxy_a_)
|
||||
}
|
||||
override fun toProxy(obj: Any) : Any {
|
||||
println ("NeedsProxyProxySerialiser - to Proxy")
|
||||
return Proxy((obj as NeedsProxy).a)
|
||||
}
|
||||
override fun fromProxy(proxy: Any) : Any = NeedsProxy((proxy as Proxy).proxy_a_)
|
||||
override fun toProxy(obj: Any) : Any = Proxy((obj as NeedsProxy).a)
|
||||
}
|
||||
|
||||
// Standard proxy serialiser used internally, here for comparison purposes
|
||||
class InternalProxySerialiser(factory: SerializerFactory) :
|
||||
CustomSerializer.Proxy<NeedsProxy, InternalProxySerialiser.Proxy> (
|
||||
// Standard proxy serializer used internally, here for comparison purposes
|
||||
class InternalProxySerializer(factory: SerializerFactory) :
|
||||
CustomSerializer.Proxy<NeedsProxy, InternalProxySerializer.Proxy> (
|
||||
NeedsProxy::class.java,
|
||||
InternalProxySerialiser.Proxy::class.java,
|
||||
InternalProxySerializer.Proxy::class.java,
|
||||
factory) {
|
||||
data class Proxy(val proxy_a_: String)
|
||||
|
||||
override fun toProxy(obj: NeedsProxy): Proxy {
|
||||
println ("InternalProxySerialiser - toProxy")
|
||||
println ("InternalProxySerializer - toProxy")
|
||||
return Proxy(obj.a)
|
||||
}
|
||||
|
||||
override fun fromProxy(proxy: Proxy): NeedsProxy {
|
||||
println ("InternalProxySerialiser - fromProxy")
|
||||
println ("InternalProxySerializer - fromProxy")
|
||||
return NeedsProxy(proxy.proxy_a_)
|
||||
}
|
||||
}
|
||||
@ -56,7 +53,7 @@ class CorDappSerializerTests {
|
||||
val msg = "help"
|
||||
|
||||
proxyFactory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), proxyFactory))
|
||||
internalProxyFactory.register (InternalProxySerialiser(internalProxyFactory))
|
||||
internalProxyFactory.register (InternalProxySerializer(internalProxyFactory))
|
||||
|
||||
val needsProxy = NeedsProxy(msg)
|
||||
|
||||
@ -89,4 +86,73 @@ class CorDappSerializerTests {
|
||||
assertEquals(tv1, objFromDefault.obj.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)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user