From 6c1442be6aae3b4b13725623f93abbbad0acd317 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Fri, 14 Jan 2022 02:35:05 +0700 Subject: [PATCH 1/2] Bump Hyperion from 0.11.2 to 0.12.0 --- src/common.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common.props b/src/common.props index 4f4f16066c1..5f69676a5e7 100644 --- a/src/common.props +++ b/src/common.props @@ -12,7 +12,7 @@ 2.4.1 17.0.0 - 0.11.2 + 0.12.0 [12.0.3,) 2.0.1 3.17.3 From 7fd25f3c3fc5987ee626cd75d432034fe626b398 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Fri, 14 Jan 2022 05:00:00 +0700 Subject: [PATCH 2/2] Implement Hyperion TypeFilter feature. Add HOCON config and HyperionSerializerSetup support. --- .../HyperionConfigTests.cs | 59 +++++++++++++++++ .../HyperionSerializerSetupSpec.cs | 43 +++++++++++++ .../HyperionSerializer.cs | 63 ++++++++++++++++--- .../HyperionSerializerSetup.cs | 38 ++++++++--- .../reference.conf | 4 ++ 5 files changed, 190 insertions(+), 17 deletions(-) diff --git a/src/contrib/serializers/Akka.Serialization.Hyperion.Tests/HyperionConfigTests.cs b/src/contrib/serializers/Akka.Serialization.Hyperion.Tests/HyperionConfigTests.cs index 2d2ed8a0727..33f8ccd1f84 100644 --- a/src/contrib/serializers/Akka.Serialization.Hyperion.Tests/HyperionConfigTests.cs +++ b/src/contrib/serializers/Akka.Serialization.Hyperion.Tests/HyperionConfigTests.cs @@ -8,10 +8,12 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; using Akka.Actor; using Akka.Configuration; using FluentAssertions; using Hyperion; +using Hyperion.Internal; using Xunit; namespace Akka.Serialization.Hyperion.Tests @@ -36,6 +38,7 @@ public void Hyperion_serializer_should_have_correct_defaults() Assert.True(serializer.Settings.PreserveObjectReferences); Assert.Equal("NoKnownTypes", serializer.Settings.KnownTypesProvider.Name); Assert.True(serializer.Settings.DisallowUnsafeType); + Assert.Equal(serializer.Settings.TypeFilter, DisabledTypeFilter.Instance); } } @@ -52,6 +55,7 @@ public void Hyperion_serializer_should_allow_to_setup_custom_flags() preserve-object-references = false version-tolerance = false disallow-unsafe-type = false + allowed-types = [""Akka.Serialization.Hyperion.Tests.HyperionConfigTests+ClassA, Akka.Serialization.Hyperion.Tests""] } } "); @@ -62,9 +66,52 @@ public void Hyperion_serializer_should_allow_to_setup_custom_flags() Assert.False(serializer.Settings.PreserveObjectReferences); Assert.Equal("NoKnownTypes", serializer.Settings.KnownTypesProvider.Name); Assert.False(serializer.Settings.DisallowUnsafeType); + Assert.Equal("Akka.Serialization.Hyperion.Tests.HyperionConfigTests+ClassA, Akka.Serialization.Hyperion.Tests", ((TypeFilter) serializer.Settings.TypeFilter).FilteredTypes.First()); } } + [Theory] + [MemberData(nameof(TypeFilterObjectFactory))] + public void TypeFilter_defined_in_config_should_filter_serializer_properly(object sampleObject, bool shouldSucceed) + { + var config = ConfigurationFactory.ParseString(@" + akka.actor { + serializers.hyperion = ""Akka.Serialization.HyperionSerializer, Akka.Serialization.Hyperion"" + serialization-bindings { + ""System.Object"" = hyperion + } + serialization-settings.hyperion { + preserve-object-references = false + version-tolerance = false + disallow-unsafe-type = true + allowed-types = [ + ""Akka.Serialization.Hyperion.Tests.HyperionConfigTests+ClassA, Akka.Serialization.Hyperion.Tests"" + ""Akka.Serialization.Hyperion.Tests.HyperionConfigTests+ClassB, Akka.Serialization.Hyperion.Tests"" + ] + } + } + "); + using (var system = ActorSystem.Create(nameof(HyperionConfigTests), config)) + { + var serializer = (HyperionSerializer)system.Serialization.FindSerializerForType(typeof(object)); + + ((TypeFilter)serializer.Settings.TypeFilter).FilteredTypes.Count.Should().Be(2); + var serialized = serializer.ToBinary(sampleObject); + object deserialized = null; + Action act = () => deserialized = serializer.FromBinary(serialized); + if (shouldSucceed) + { + act.Should().NotThrow(); + deserialized.GetType().Should().Be(sampleObject.GetType()); + } + else + { + act.Should().Throw() + .WithInnerException(); + } + } + } + [Fact] public void Hyperion_serializer_should_allow_to_setup_custom_types_provider_with_default_constructor() { @@ -198,6 +245,18 @@ public void Hyperion_serializer_should_allow_to_setup_surrogates() } } + public static IEnumerable TypeFilterObjectFactory() + { + yield return new object[] { new ClassA(), true }; + yield return new object[] { new ClassB(), true }; + yield return new object[] { new ClassC(), false }; + } + + public class ClassA { } + + public class ClassB { } + + public class ClassC { } } class DummyTypesProvider : IKnownTypesProvider diff --git a/src/contrib/serializers/Akka.Serialization.Hyperion.Tests/HyperionSerializerSetupSpec.cs b/src/contrib/serializers/Akka.Serialization.Hyperion.Tests/HyperionSerializerSetupSpec.cs index 6c203dc7e46..a5e0cc8be2e 100644 --- a/src/contrib/serializers/Akka.Serialization.Hyperion.Tests/HyperionSerializerSetupSpec.cs +++ b/src/contrib/serializers/Akka.Serialization.Hyperion.Tests/HyperionSerializerSetupSpec.cs @@ -16,6 +16,7 @@ using Xunit.Abstractions; using FluentAssertions; using Hyperion; +using Hyperion.Internal; namespace Akka.Serialization.Hyperion.Tests { @@ -134,6 +135,35 @@ public void Setup_disallow_unsafe_type_should_work(object dangerousObject, Type serializer.Invoking(s => s.FromBinary(serialized, type)).Should().Throw(); } + [Theory] + [MemberData(nameof(TypeFilterObjectFactory))] + public void Setup_TypeFilter_should_filter_types_properly(object sampleObject, bool shouldSucceed) + { + var setup = HyperionSerializerSetup.Empty + .WithTypeFilter(TypeFilterBuilder.Create() + .Include() + .Include() + .Build()); + + var settings = setup.ApplySettings(HyperionSerializerSettings.Default); + var serializer = new HyperionSerializer((ExtendedActorSystem)Sys, settings); + + ((TypeFilter)serializer.Settings.TypeFilter).FilteredTypes.Count.Should().Be(2); + var serialized = serializer.ToBinary(sampleObject); + object deserialized = null; + Action act = () => deserialized = serializer.FromBinary(serialized); + if (shouldSucceed) + { + act.Should().NotThrow(); + deserialized.GetType().Should().Be(sampleObject.GetType()); + } + else + { + act.Should().Throw() + .WithInnerException(); + } + } + public static IEnumerable DangerousObjectFactory() { var isWindow = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); @@ -150,5 +180,18 @@ public static IEnumerable DangerousObjectFactory() #endif yield return new object[]{ new ClaimsIdentity(), typeof(ClaimsIdentity)}; } + + public static IEnumerable TypeFilterObjectFactory() + { + yield return new object[] { new ClassA(), true }; + yield return new object[] { new ClassB(), true }; + yield return new object[] { new ClassC(), false }; + } + + public class ClassA { } + + public class ClassB { } + + public class ClassC { } } } diff --git a/src/contrib/serializers/Akka.Serialization.Hyperion/HyperionSerializer.cs b/src/contrib/serializers/Akka.Serialization.Hyperion/HyperionSerializer.cs index d7f85caa4f1..bd8c0962700 100644 --- a/src/contrib/serializers/Akka.Serialization.Hyperion/HyperionSerializer.cs +++ b/src/contrib/serializers/Akka.Serialization.Hyperion/HyperionSerializer.cs @@ -87,7 +87,8 @@ public HyperionSerializer(ExtendedActorSystem system, HyperionSerializerSettings knownTypes: provider.GetKnownTypes(), ignoreISerializable:true, packageNameOverrides: settings.PackageNameOverrides, - disallowUnsafeTypes: settings.DisallowUnsafeType)); + disallowUnsafeTypes: settings.DisallowUnsafeType, + typeFilter: settings.TypeFilter)); } /// @@ -166,7 +167,8 @@ public sealed class HyperionSerializerSettings knownTypesProvider: typeof(NoKnownTypes), packageNameOverrides: new List>(), surrogates: new Surrogate[0], - disallowUnsafeType: true); + disallowUnsafeType: true, + typeFilter: DisabledTypeFilter.Instance); /// /// Creates a new instance of using provided HOCON config. @@ -227,7 +229,22 @@ public static HyperionSerializerSettings Create(Config config) surrogates.Add((Surrogate)Activator.CreateInstance(surrogateType)); } - + + var typeFilter = (ITypeFilter) DisabledTypeFilter.Instance; + var allowedList = config.GetStringList("allowed-types"); + if (allowedList.Count > 0) + { + var filterBuilder = TypeFilterBuilder.Create(); + foreach (var allowedFqcn in allowedList) + { + var allowedType = Type.GetType(allowedFqcn); + if (allowedType is null) + throw new ConfigurationException($"Could not load type [{allowedFqcn}] from allowed-type."); + filterBuilder.Include(allowedType); + } + + typeFilter = filterBuilder.Build(); + } return new HyperionSerializerSettings( preserveObjectReferences: config.GetBoolean("preserve-object-references", true), @@ -235,7 +252,8 @@ public static HyperionSerializerSettings Create(Config config) knownTypesProvider: type, packageNameOverrides: packageNameOverrides, surrogates: surrogates, - disallowUnsafeType: config.GetBoolean("disallow-unsafe-type", true)); + disallowUnsafeType: config.GetBoolean("disallow-unsafe-type", true), + typeFilter: typeFilter); } /// @@ -278,6 +296,12 @@ public static HyperionSerializerSettings Create(Config config) /// public readonly bool DisallowUnsafeType; + /// + /// If set, Hyperion serializer will use the to filter the types that are + /// being deserialized during run-time + /// + public readonly ITypeFilter TypeFilter; + /// /// Creates a new instance of a . /// @@ -287,7 +311,7 @@ public static HyperionSerializerSettings Create(Config config) /// Raised when `known-types-provider` type doesn't implement interface. [Obsolete] public HyperionSerializerSettings(bool preserveObjectReferences, bool versionTolerance, Type knownTypesProvider) - : this(preserveObjectReferences, versionTolerance, knownTypesProvider, new List>(), new Surrogate[0], true) + : this(preserveObjectReferences, versionTolerance, knownTypesProvider, new List>(), new Surrogate[0], true, DisabledTypeFilter.Instance) { } /// @@ -304,7 +328,7 @@ public HyperionSerializerSettings( bool versionTolerance, Type knownTypesProvider, IEnumerable> packageNameOverrides) - : this(preserveObjectReferences, versionTolerance, knownTypesProvider, packageNameOverrides, new Surrogate[0], true) + : this(preserveObjectReferences, versionTolerance, knownTypesProvider, packageNameOverrides, new Surrogate[0], true, DisabledTypeFilter.Instance) { } /// @@ -322,7 +346,27 @@ public HyperionSerializerSettings( Type knownTypesProvider, IEnumerable> packageNameOverrides, IEnumerable surrogates) - : this(preserveObjectReferences, versionTolerance, knownTypesProvider, packageNameOverrides, surrogates, true) + : this(preserveObjectReferences, versionTolerance, knownTypesProvider, packageNameOverrides, surrogates, true, DisabledTypeFilter.Instance) + { } + + /// + /// Creates a new instance of a . + /// + /// Flag which determines if serializer should keep track of references in serialized object graph. + /// Flag which determines if field data should be serialized as part of type manifest. + /// Type implementing to be used to determine a list of types implicitly known by all cooperating serializer. + /// An array of package name overrides for cross platform compatibility + /// A list of instances that are used to de/serialize complex objects into a much simpler serialized objects. + /// Block unsafe types from being deserialized. + /// Raised when `known-types-provider` type doesn't implement interface. + public HyperionSerializerSettings( + bool preserveObjectReferences, + bool versionTolerance, + Type knownTypesProvider, + IEnumerable> packageNameOverrides, + IEnumerable surrogates, + bool disallowUnsafeType) + : this(preserveObjectReferences, versionTolerance, knownTypesProvider, packageNameOverrides, surrogates, disallowUnsafeType, DisabledTypeFilter.Instance) { } /// @@ -334,6 +378,7 @@ public HyperionSerializerSettings( /// An array of package name overrides for cross platform compatibility /// A list of instances that are used to de/serialize complex objects into a much simpler serialized objects. /// Block unsafe types from being deserialized. + /// A instance that will filter types from being deserialized. /// Raised when `known-types-provider` type doesn't implement interface. public HyperionSerializerSettings( bool preserveObjectReferences, @@ -341,7 +386,8 @@ public HyperionSerializerSettings( Type knownTypesProvider, IEnumerable> packageNameOverrides, IEnumerable surrogates, - bool disallowUnsafeType) + bool disallowUnsafeType, + ITypeFilter typeFilter) { knownTypesProvider = knownTypesProvider ?? typeof(NoKnownTypes); if (!typeof(IKnownTypesProvider).IsAssignableFrom(knownTypesProvider)) @@ -353,6 +399,7 @@ public HyperionSerializerSettings( PackageNameOverrides = packageNameOverrides; Surrogates = surrogates; DisallowUnsafeType = disallowUnsafeType; + TypeFilter = typeFilter; } } } diff --git a/src/contrib/serializers/Akka.Serialization.Hyperion/HyperionSerializerSetup.cs b/src/contrib/serializers/Akka.Serialization.Hyperion/HyperionSerializerSetup.cs index d7c781b7731..d5319238fd3 100644 --- a/src/contrib/serializers/Akka.Serialization.Hyperion/HyperionSerializerSetup.cs +++ b/src/contrib/serializers/Akka.Serialization.Hyperion/HyperionSerializerSetup.cs @@ -17,20 +17,20 @@ namespace Akka.Serialization.Hyperion public class HyperionSerializerSetup : Setup { public static readonly HyperionSerializerSetup Empty = - new HyperionSerializerSetup(Option.None, Option.None, null, null, null, Option.None); + new HyperionSerializerSetup(Option.None, Option.None, null, null, null, Option.None, DisabledTypeFilter.Instance); public static HyperionSerializerSetup Create( bool preserveObjectReferences, bool versionTolerance, Type knownTypesProvider) - => new HyperionSerializerSetup(preserveObjectReferences, versionTolerance, knownTypesProvider, null, null, Option.None); + => new HyperionSerializerSetup(preserveObjectReferences, versionTolerance, knownTypesProvider, null, null, Option.None, DisabledTypeFilter.Instance); public static HyperionSerializerSetup Create( bool preserveObjectReferences, bool versionTolerance, Type knownTypesProvider, IEnumerable> packageNameOverrides) - => new HyperionSerializerSetup(preserveObjectReferences, versionTolerance, knownTypesProvider, packageNameOverrides, null, Option.None); + => new HyperionSerializerSetup(preserveObjectReferences, versionTolerance, knownTypesProvider, packageNameOverrides, null, Option.None, DisabledTypeFilter.Instance); public static HyperionSerializerSetup Create( bool preserveObjectReferences, @@ -38,7 +38,7 @@ public static HyperionSerializerSetup Create( Type knownTypesProvider, IEnumerable> packageNameOverrides, IEnumerable surrogates) - => new HyperionSerializerSetup(preserveObjectReferences, versionTolerance, knownTypesProvider, packageNameOverrides, surrogates, Option.None); + => new HyperionSerializerSetup(preserveObjectReferences, versionTolerance, knownTypesProvider, packageNameOverrides, surrogates, Option.None, DisabledTypeFilter.Instance); public static HyperionSerializerSetup Create( bool preserveObjectReferences, @@ -47,7 +47,17 @@ public static HyperionSerializerSetup Create( IEnumerable> packageNameOverrides, IEnumerable surrogates, bool disallowUnsafeType) - => new HyperionSerializerSetup(preserveObjectReferences, versionTolerance, knownTypesProvider, packageNameOverrides, surrogates, disallowUnsafeType); + => new HyperionSerializerSetup(preserveObjectReferences, versionTolerance, knownTypesProvider, packageNameOverrides, surrogates, disallowUnsafeType, DisabledTypeFilter.Instance); + + public static HyperionSerializerSetup Create( + bool preserveObjectReferences, + bool versionTolerance, + Type knownTypesProvider, + IEnumerable> packageNameOverrides, + IEnumerable surrogates, + bool disallowUnsafeType, + ITypeFilter typeFilter) + => new HyperionSerializerSetup(preserveObjectReferences, versionTolerance, knownTypesProvider, packageNameOverrides, surrogates, disallowUnsafeType, typeFilter); private HyperionSerializerSetup( Option preserveObjectReferences, @@ -55,7 +65,8 @@ private HyperionSerializerSetup( Type knownTypesProvider, IEnumerable> packageNameOverrides, IEnumerable surrogates, - Option disallowUnsafeType) + Option disallowUnsafeType, + ITypeFilter typeFilter) { PreserveObjectReferences = preserveObjectReferences; VersionTolerance = versionTolerance; @@ -63,6 +74,7 @@ private HyperionSerializerSetup( PackageNameOverrides = packageNameOverrides; Surrogates = surrogates; DisallowUnsafeType = disallowUnsafeType; + TypeFilter = typeFilter; } /// @@ -105,6 +117,8 @@ private HyperionSerializerSetup( /// from being deserialized during run-time. Defaults to true. /// public Option DisallowUnsafeType { get; } + + public ITypeFilter TypeFilter { get; } internal HyperionSerializerSettings ApplySettings(HyperionSerializerSettings settings) => new HyperionSerializerSettings( @@ -113,7 +127,8 @@ internal HyperionSerializerSettings ApplySettings(HyperionSerializerSettings set KnownTypesProvider ?? settings.KnownTypesProvider, PackageNameOverrides ?? settings.PackageNameOverrides, Surrogates ?? settings.Surrogates, - DisallowUnsafeType.HasValue ? DisallowUnsafeType.Value : settings.DisallowUnsafeType + DisallowUnsafeType.HasValue ? DisallowUnsafeType.Value : settings.DisallowUnsafeType, + TypeFilter ?? settings.TypeFilter ); public HyperionSerializerSetup WithPreserveObjectReference(bool preserveObjectReference) @@ -137,13 +152,17 @@ public HyperionSerializerSetup WithSurrogates(IEnumerable surrogates) public HyperionSerializerSetup WithDisallowUnsafeType(bool disallowUnsafeType) => Copy(disallowUnsafeType: disallowUnsafeType); + public HyperionSerializerSetup WithTypeFilter(ITypeFilter typeFilter) + => Copy(typeFilter: typeFilter); + private HyperionSerializerSetup Copy( bool? preserveObjectReferences = null, bool? versionTolerance = null, Type knownTypesProvider = null, IEnumerable> packageNameOverrides = null, IEnumerable surrogates = null, - bool? disallowUnsafeType = null + bool? disallowUnsafeType = null, + ITypeFilter typeFilter = null ) => new HyperionSerializerSetup( preserveObjectReferences ?? PreserveObjectReferences, @@ -151,6 +170,7 @@ private HyperionSerializerSetup Copy( knownTypesProvider ?? KnownTypesProvider, packageNameOverrides ?? PackageNameOverrides, surrogates ?? Surrogates, - disallowUnsafeType ?? DisallowUnsafeType); + disallowUnsafeType ?? DisallowUnsafeType, + typeFilter ?? TypeFilter); } } diff --git a/src/contrib/serializers/Akka.Serialization.Hyperion/reference.conf b/src/contrib/serializers/Akka.Serialization.Hyperion/reference.conf index 6817aca475f..eccf4724575 100644 --- a/src/contrib/serializers/Akka.Serialization.Hyperion/reference.conf +++ b/src/contrib/serializers/Akka.Serialization.Hyperion/reference.conf @@ -30,6 +30,10 @@ # constructor or constructor accepting an `ExtendedActorSystem` as its only parameter. known-types-provider = "Akka.Serialization.NoKnownTypes, Akka.Serialization.Hyperion" + # An array of fully qualified class names of types that are allowed to be deserialized + # during run-time. When left as an empty array, type filtering will be disabled. + allowed-types = [] + # A list of incompatible dll package name for deserializing types # between NetFx, .NET Core, and the new .NET # Used to map and rename/correct different dll names between different platforms