Uploaded image for project: 'C# Driver'
  1. C# Driver
  2. CSHARP-4820

Configured serializer is not called in some cases, resulting in incorrect serialization

    • Type: Icon: Bug Bug
    • Resolution: Unresolved
    • Priority: Icon: Minor - P4 Minor - P4
    • None
    • Affects Version/s: None
    • Component/s: Serialization
    • Labels:
      None
    • Hide

      1. What would you like to communicate to the user about this feature?
      2. Would you like the user to see examples of the syntax and/or executable code and its output?
      3. Which versions of the driver/connector does this apply to?

      Show
      1. What would you like to communicate to the user about this feature? 2. Would you like the user to see examples of the syntax and/or executable code and its output? 3. Which versions of the driver/connector does this apply to?

      Summary

      C# driver doesn't work with IReadOnlyCollection<s> types.

      Customer is implementing CSFLE with C# driver, It is working with fields and generic IEnumerable<> types but recently a new feature tried to use IReadOnlyCollection<s> which should have been encrypted, but is not. They think the issue is somewhere in the C# driver as they are not getting called back into PropertyEncrypterBsonSerializer. Adding a .ToList() to the update operation ( after the Select()) fixes the issue.

      Please provide the version of the driver. If applicable, please provide the MongoDB server version and topology (standalone, replica set, or sharded cluster).

      MongoDB 7.0.2 (Replica Set)
      net6.0 (also LINQv2)
      C# Driver 2.18.0

      How to Reproduce

      Steps to reproduce. If possible, please include a Short, Self Contained, Correct (Compilable), Example.

      Here is the sample code customer is using:
      __

      public class EncryptedPropertiesConvention : ConventionBase, IPostProcessingConvention
      {
          public const string ConventionName = "Encryption";
          private readonly IKeyResolver _keyResolver;
          private readonly ConcurrentDictionary<SecretId, AsyncLazy<SymmetricKeyDto[]>> _keyCache;
          private readonly ConcurrentDictionary<Type, IBsonSerializer> _serializerCache;
      
          private EncryptedPropertiesConvention(IKeyResolver keyResolver)
          {
              this._keyResolver = keyResolver;
              this._keyCache = new ConcurrentDictionary<SecretId, AsyncLazy<SymmetricKeyDto[]>>();
              this._serializerCache = new ConcurrentDictionary<Type, IBsonSerializer>();
          }
      
          /// <summary>
          /// Applies a post processing modification to the class map.
          /// </summary>
          /// <param name="classMap">The class map.</param>
          public void PostProcess(BsonClassMap classMap)
          {
              foreach (var memberMap in classMap.DeclaredMemberMaps.Where(x => x.MemberInfo.GetCustomAttribute<SensitiveInformationPropertyAttribute>() != null))
              {
                  var serializer = memberMap.GetSerializer();
                  memberMap.SetSerializer(this.CreateSerializer(serializer));
              }
          }
      
          public static void Register(IKeyResolver keyResolver)
          {
              // Clear pre-existing encryption before activating new encryption
              ConventionRegistry.Remove(ConventionName);
      
              ConventionRegistry.Register(
                  ConventionName,
                  new ConventionPack
                  {
                      new EncryptedPropertiesConvention(keyResolver),
                  },
                  EncryptionHelper.HasPropertiesToEncrypt);
          }
      
          private IBsonSerializer CreateSerializer(IBsonSerializer serializer)
          {
              IBsonSerializer Create(Type t)
              {
                  var serializerType = typeof(PropertyEncrypterBsonSerializer<>).MakeGenericType(serializer.ValueType);
                  return (IBsonSerializer)Activator.CreateInstance(serializerType, serializer, this._keyResolver, this._keyCache);
              }
      
              return this._serializerCache.GetOrAdd(serializer.ValueType, Create);
          }
      }`
       

      For exemple, a document that has the following properties will only encrypt the IEnumerable<s> type:

      __

      public class MyMongoCollection 
      { 
          public Guid PrimaryKey { get; set; } 
          [SensitiveInformationProperty] public IEnumerable<string> UserEnumerable { get; set; } = new List<string>(); 
          [SensitiveInformationProperty] public IReadOnlyCollection<string> UserReadOnly { get; set; } = new List<string>(); 
          [SensitiveInformationProperty] public IList<string> UserList { get; set; } = new List<string>(); } 
      
          public class User 
          { 
              public User(string name, string email) 
              { 
                  this.Name = name; 
                  this.Email = email; 
              } 
      
              public string Name { get; set; } 
              public string Email { get; set; } } 
      
              private void SimpleUpsert(Guid primaryKey) 
              { 
                  var filter = Builders<MyMongoCollection>.Filter.Eq(x => x.PrimaryKey, primaryKey); 
                  User[] usersData = { new User("George", "george@email.com") }; 
                  var update = Builders<MyMongoCollection>.Update 
                      .SetOnInsert(d => d.PrimaryKey, primaryKey) 
                      .Set(d => d.UserEnumerable, usersData?.Select(x => x.Email)) 
                      .Set(d => d.UserReadOnly, usersData?.Select(x => x.Email)) 
                      .Set(d => d.UserList, usersData?.Select(x => x.Email)); 
      
              // Fetch the collection with the driver 
              // then callls 
              // await collection.UpdateOneAsync(filter, update, new UpdateOptions { IsUpsert = true }); } 
      

       

            Assignee:
            robert@mongodb.com Robert Stam
            Reporter:
            ladan.nekuii@mongodb.com Ladan Nekuii
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: