From d72c6774e6a13de2cfcd7d477d3575efeb75c8f2 Mon Sep 17 00:00:00 2001 From: Leonardo Nascimento Date: Mon, 5 Dec 2022 14:11:22 -0300 Subject: [PATCH] Fix object deserialization when type is not assignable --- LiteDB.Tests/Mapper/Mapper_Tests.cs | 30 +++++++++++++++++++ .../Client/Mapper/BsonMapper.Deserialize.cs | 7 +++-- LiteDB/Utils/LiteException.cs | 6 ++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/LiteDB.Tests/Mapper/Mapper_Tests.cs b/LiteDB.Tests/Mapper/Mapper_Tests.cs index 043e2fa85..9dfd667f4 100644 --- a/LiteDB.Tests/Mapper/Mapper_Tests.cs +++ b/LiteDB.Tests/Mapper/Mapper_Tests.cs @@ -1,4 +1,6 @@ using FluentAssertions; +using System; +using System.Reflection; using Xunit; namespace LiteDB.Tests.Mapper @@ -17,5 +19,33 @@ public void ToDocument_ReturnsNull_WhenFail() var doc2 = _mapper.ToDocument(typeof(int[]), array); doc2.Should().Be(null); } + + [Fact] + public void Class_Not_Assignable() + { + using (var db = new LiteDatabase(":memory:")) + { + var col = db.GetCollection("Test"); + col.Insert(new MyClass { Id = 1, Member = null }); + var type = typeof(OtherClass); + var typeName = type.FullName + ", " + type.GetTypeInfo().Assembly.GetName().Name; + + db.Execute($"update Test set Member = {{_id: 1, Name: null, _type: \"{typeName}\"}} where _id = 1"); + + Func func = (() => col.FindById(1)); + func.Should().Throw(); + } + } + + public class MyClass + { + public int Id { get; set; } + public MyClass Member { get; set; } + } + + public class OtherClass + { + public string Name { get; set; } + } } } diff --git a/LiteDB/Client/Mapper/BsonMapper.Deserialize.cs b/LiteDB/Client/Mapper/BsonMapper.Deserialize.cs index d2e23943b..5660db622 100644 --- a/LiteDB/Client/Mapper/BsonMapper.Deserialize.cs +++ b/LiteDB/Client/Mapper/BsonMapper.Deserialize.cs @@ -167,9 +167,12 @@ public object Deserialize(Type type, BsonValue value) // test if value is object and has _type if (doc.TryGetValue("_type", out var typeField) && typeField.IsString) { - type = _typeNameBinder.GetType(typeField.AsString); + var actualType = _typeNameBinder.GetType(typeField.AsString); - if (type == null) throw LiteException.InvalidTypedName(typeField.AsString); + if (actualType == null) throw LiteException.InvalidTypedName(typeField.AsString); + if (!type.IsAssignableFrom(actualType)) throw LiteException.DataTypeNotAssignable(type.FullName, actualType.FullName); + + type = actualType; } // when complex type has no definition (== typeof(object)) use Dictionary to better set values else if (type == typeof(object)) diff --git a/LiteDB/Utils/LiteException.cs b/LiteDB/Utils/LiteException.cs index bf5a4f003..a27dc3c85 100644 --- a/LiteDB/Utils/LiteException.cs +++ b/LiteDB/Utils/LiteException.cs @@ -51,6 +51,7 @@ public class LiteException : Exception public const int INVALID_INITIALSIZE = 211; public const int INVALID_NULL_CHAR_STRING = 212; public const int INVALID_FREE_SPACE_PAGE = 213; + public const int DATA_TYPE_NOT_ASSIGNABLE = 214; #endregion @@ -306,6 +307,11 @@ internal static LiteException InvalidFreeSpacePage(uint pageID, int freeBytes, i return new LiteException(INVALID_FREE_SPACE_PAGE, $"An operation that would corrupt page {pageID} was prevented. The operation required {length} free bytes, but the page had only {freeBytes} available."); } + internal static LiteException DataTypeNotAssignable(string type1, string type2) + { + return new LiteException(DATA_TYPE_NOT_ASSIGNABLE, $"Data type {type1} is not assignable from data type {type2}"); + } + #endregion } } \ No newline at end of file