Browse Source

Removed old things

pull/1631/head
Ferdinando Papale 4 months ago
parent
commit
797095815e
  1. 553
      src/MongoDB.Driver/Encryption/CsfleSchemaBuilder.cs
  2. 595
      tests/MongoDB.Driver.Tests/Encryption/CsfleSchemaBuilderTests.cs

553
src/MongoDB.Driver/Encryption/CsfleSchemaBuilder.cs

@ -22,260 +22,92 @@ using MongoDB.Bson.Serialization;
namespace MongoDB.Driver.Encryption
{
//TODO Add docs
//TODO BsonType can be multiple types in some cases
/// <summary>
///
/// </summary>
public class CsfleSchemaBuilder
{
private readonly Dictionary<string, CsfleTypeSchemaBuilder> _typeSchemaBuilders = new();
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static CsfleTypeSchemaBuilder<T> GetTypeBuilder<T>() => new(); //TODO Do we need this?
private readonly Dictionary<string, TypedBuilder> _typedBuilders = [];
private CsfleSchemaBuilder()
{
}
/// <summary>
///
/// </summary>
/// <param name="collectionNamespace">The namespace to which the encryption schema applies.</param>
/// <param name="typedBuilder"></param>
/// <typeparam name="T"></typeparam>
/// <param name="configure"></param>
/// <returns></returns>
public CsfleSchemaBuilder WithType<T>(CollectionNamespace collectionNamespace, CsfleTypeSchemaBuilder<T> typedBuilder)
public static CsfleSchemaBuilder Create(Action<CsfleSchemaBuilder> configure)
{
_typeSchemaBuilders.Add(collectionNamespace.FullName, typedBuilder);
return this;
var builder = new CsfleSchemaBuilder();
configure(builder);
return builder;
}
/// <summary>
///
/// </summary>
/// <param name="collectionNamespace">The namespace to which the encryption schema applies.</param>
/// <param name="collectionNamespace"></param>
/// <param name="configure"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public CsfleSchemaBuilder WithType<T>(CollectionNamespace collectionNamespace, Action<CsfleTypeSchemaBuilder<T>> configure) //TODO Do we want to keep this?
/// <typeparam name="TDocument"></typeparam>
public void Encrypt<TDocument>(CollectionNamespace collectionNamespace, Action<TypedBuilder<TDocument>> configure)
{
var typedBuilder = new CsfleTypeSchemaBuilder<T>();
var typedBuilder = new TypedBuilder<TDocument>();
configure(typedBuilder);
_typeSchemaBuilders.Add(collectionNamespace.FullName, typedBuilder);
return this;
_typedBuilders.Add(collectionNamespace.FullName, typedBuilder);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public IReadOnlyDictionary<string, BsonDocument> Build() => _typeSchemaBuilders.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Build());
}
/// <summary>
///
/// </summary>
public abstract class CsfleTypeSchemaBuilder
{
/// <summary>
///
/// </summary>
/// <returns></returns>
public abstract BsonDocument Build();
public IReadOnlyDictionary<string, BsonDocument> Build() => _typedBuilders.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Build());
}
/// <summary>
///
/// </summary>
/// <typeparam name="TDocument"></typeparam>
public class CsfleTypeSchemaBuilder<TDocument> : CsfleTypeSchemaBuilder
/// <typeparam name="TSelf"></typeparam>
public class ElementBuilder<TSelf> where TSelf : ElementBuilder<TSelf>
{
private readonly List<SchemaField> _fields = [];
private readonly List<SchemaPattern> _patterns = [];
private SchemaMetadata _metadata;
/// <summary>
///
/// </summary>
/// <param name="path">The field to be encrypted.</param>
/// <param name="keyId">The id of the Data Encryption Key to use for encrypting.</param>
/// <param name="algorithm">The encryption algorithm to use.</param>
/// <param name="bsonType">The BSON type of the field being encrypted.</param>
/// <returns></returns>
public CsfleTypeSchemaBuilder<TDocument> Property(FieldDefinition<TDocument> path, Guid? keyId = null, CsfleEncryptionAlgorithm? algorithm = null, BsonType? bsonType = null)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
_fields.Add(new SchemaSimpleField(path, keyId, algorithm, bsonType));
return this;
}
/// <summary>
///
/// </summary>
/// <param name="path">The field to be encrypted.</param>
/// <param name="keyId">The id of the Data Encryption Key to use for encrypting.</param>
/// <param name="algorithm">The encryption algorithm to use.</param>
/// <param name="bsonType">The BSON type of the field being encrypted.</param>
/// <typeparam name="TField"></typeparam>
/// <returns></returns>
public CsfleTypeSchemaBuilder<TDocument> Property<TField>(Expression<Func<TDocument, TField>> path, Guid? keyId = null, CsfleEncryptionAlgorithm? algorithm = null, BsonType? bsonType = null)
{
return Property(new ExpressionFieldDefinition<TDocument, TField>(path), keyId, algorithm, bsonType);
}
/// <summary>
///
/// </summary>
/// <param name="path">The field to use for the nested property.</param>
/// <param name="configure"></param>
/// <typeparam name="TField"></typeparam>
/// <returns></returns>
public CsfleTypeSchemaBuilder<TDocument> Property<TField>(FieldDefinition<TDocument> path, Action<CsfleTypeSchemaBuilder<TField>> configure)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
_fields.Add(new SchemaNestedField<TField>(path, configure));
return this;
}
/// <summary>
///
/// </summary>
/// <param name="path">The field to be encrypted.</param>
/// <param name="configure"></param>
/// <typeparam name="TField"></typeparam>
/// <returns></returns>
public CsfleTypeSchemaBuilder<TDocument> Property<TField>(Expression<Func<TDocument, TField>> path, Action<CsfleTypeSchemaBuilder<TField>> configure)
{
return Property(new ExpressionFieldDefinition<TDocument, TField>(path), configure);
}
/// <summary>
///
/// </summary>
/// <param name="pattern">The pattern to use.</param>
/// <param name="keyId">The id of the Data Encryption Key to use for encrypting.</param>
/// <param name="algorithm">The encryption algorithm to use.</param>
/// <param name="bsonType">The BSON type of the field being encrypted.</param>
/// <returns></returns>
public CsfleTypeSchemaBuilder<TDocument> PatternProperty(string pattern, Guid? keyId = null, CsfleEncryptionAlgorithm? algorithm = null, BsonType? bsonType = null)
{
if (string.IsNullOrWhiteSpace(pattern))
{
throw new ArgumentException("Input pattern cannot be empty or null", nameof(pattern));
}
_patterns.Add(new SchemaSimplePattern(pattern, keyId, algorithm, bsonType));
return this;
}
/// <summary>
///
/// </summary>
/// <param name="path">The field to use for the nested pattern property.</param>
/// <param name="configure"></param>
/// <typeparam name="TField"></typeparam>
/// <returns></returns>
public CsfleTypeSchemaBuilder<TDocument> PatternProperty<TField>(FieldDefinition<TDocument> path, Action<CsfleTypeSchemaBuilder<TField>> configure)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
_patterns.Add(new SchemaNestedPattern<TField>(path, configure));
return this;
}
internal CsfleEncryptionAlgorithm? _algorithm; //TODO These should be protected as well
internal Guid? _keyId;
/// <summary>
///
/// </summary>
/// <param name="path">The field to use for the nested pattern property.</param>
/// <param name="configure"></param>
/// <typeparam name="TField"></typeparam>
/// <param name="keyId"></param>
/// <returns></returns>
public CsfleTypeSchemaBuilder<TDocument> PatternProperty<TField>(Expression<Func<TDocument, TField>> path, Action<CsfleTypeSchemaBuilder<TField>> configure)
public TSelf WithKeyId(Guid keyId)
{
return PatternProperty(new ExpressionFieldDefinition<TDocument, TField>(path), configure);
_keyId = keyId;
return (TSelf)this;
}
/// <summary>
///
/// </summary>
/// <param name="keyId">The id of the Data Encryption Key to use for encrypting.</param>
/// <param name="algorithm">The encryption algorithm to use.</param>
/// <param name="algorithm"></param>
/// <returns></returns>
public CsfleTypeSchemaBuilder<TDocument> EncryptMetadata(Guid? keyId = null, CsfleEncryptionAlgorithm? algorithm = null )
public TSelf WithAlgorithm(CsfleEncryptionAlgorithm algorithm)
{
_metadata = new SchemaMetadata(keyId, algorithm);
return this;
_algorithm = algorithm;
return (TSelf)this;
}
/// <inheritdoc />
public override BsonDocument Build()
internal static BsonDocument GetEncryptBsonDocument(Guid? keyId, CsfleEncryptionAlgorithm? algorithm, List<BsonType> bsonTypes)
{
var schema = new BsonDocument("bsonType", "object");
var bsonType = bsonTypes?.First(); //TODO need to support multiple types
if (_metadata is not null)
{
schema.Merge(_metadata.Build());
}
var args = new RenderArgs<TDocument>(BsonSerializer.LookupSerializer<TDocument>(), BsonSerializer.SerializerRegistry);
if (_fields.Any())
{
var properties = new BsonDocument();
foreach (var field in _fields)
{
properties.Merge(field.Build(args));
}
schema.Add("properties", properties);
}
if (_patterns.Any())
return new BsonDocument
{
var patternProperties = new BsonDocument();
foreach (var pattern in _patterns)
{ "bsonType", () => MapBsonTypeToString(bsonType!.Value), bsonType is not null },
{ "algorithm", () => MapCsfleEncyptionAlgorithmToString(algorithm!.Value), algorithm is not null },
{
patternProperties.Merge(pattern.Build(args));
}
schema.Add("patternProperties", patternProperties);
}
return schema;
}
private static string MapCsfleEncyptionAlgorithmToString(CsfleEncryptionAlgorithm algorithm)
{
return algorithm switch
{
CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random => "AEAD_AES_256_CBC_HMAC_SHA_512-Random",
CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic => "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
_ => throw new ArgumentException($"Unexpected algorithm type: {algorithm}.", nameof(algorithm))
"keyId",
() => new BsonArray(new[] { new BsonBinaryData(keyId!.Value, GuidRepresentation.Standard) }),
keyId is not null
},
};
}
@ -307,157 +139,308 @@ namespace MongoDB.Driver.Encryption
};
}
private abstract record SchemaField
{
public abstract BsonDocument Build(RenderArgs<TDocument> args);
}
private record SchemaSimpleField(FieldDefinition<TDocument> Path, Guid? KeyId, CsfleEncryptionAlgorithm? Algorithm, BsonType? BsonType) : SchemaField
{
public override BsonDocument Build(RenderArgs<TDocument> args) =>
new(Path.Render(args).FieldName, new BsonDocument("encrypt", GetEncryptBsonDocument(KeyId, Algorithm, BsonType)));
}
private record SchemaNestedField<TField>(FieldDefinition<TDocument> Path, Action<CsfleTypeSchemaBuilder<TField>> Configure) : SchemaField
private static string MapCsfleEncyptionAlgorithmToString(CsfleEncryptionAlgorithm algorithm)
{
public override BsonDocument Build(RenderArgs<TDocument> args)
return algorithm switch
{
var fieldBuilder = new CsfleTypeSchemaBuilder<TField>();
Configure(fieldBuilder);
return new BsonDocument(Path.Render(args).FieldName, fieldBuilder.Build());
}
CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random => "AEAD_AES_256_CBC_HMAC_SHA_512-Random",
CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic => "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
_ => throw new ArgumentException($"Unexpected algorithm type: {algorithm}.", nameof(algorithm))
};
}
}
private abstract record SchemaPattern
{
public abstract BsonDocument Build(RenderArgs<TDocument> args);
}
/// <summary>
///
/// </summary>
public class EncryptMetadataBuilder : ElementBuilder<EncryptMetadataBuilder>
{
internal BsonDocument Build() => new("encryptMetadata", GetEncryptBsonDocument(_keyId, _algorithm, null));
}
private record SchemaSimplePattern(
string Pattern,
Guid? KeyId,
CsfleEncryptionAlgorithm? Algorithm,
BsonType? BsonType) : SchemaPattern
/// <summary>
///
/// </summary>
/// <typeparam name="TDocument"></typeparam>
public class PropertyBuilder<TDocument> : ElementBuilder<PropertyBuilder<TDocument>> //TODO Maybe we can have a common class for this and patternPropertyBuilder
{
private readonly FieldDefinition<TDocument> _path;
private List<BsonType> _bsonTypes;
/// <summary>
///
/// </summary>
/// <param name="path"></param>
public PropertyBuilder(FieldDefinition<TDocument> path)
{
public override BsonDocument Build(RenderArgs<TDocument> args) => new(Pattern, new BsonDocument("encrypt", GetEncryptBsonDocument(KeyId, Algorithm, BsonType)));
_path = path;
}
private record SchemaNestedPattern<TField>(
FieldDefinition<TDocument> Path,
Action<CsfleTypeSchemaBuilder<TField>> Configure) : SchemaPattern
/// <summary>
///
/// </summary>
/// <param name="bsonType"></param>
/// <returns></returns>
public PropertyBuilder<TDocument> WithBsonType(BsonType bsonType)
{
public override BsonDocument Build(RenderArgs<TDocument> args)
{
var fieldBuilder = new CsfleTypeSchemaBuilder<TField>();
Configure(fieldBuilder);
return new BsonDocument(Path.Render(args).FieldName, fieldBuilder.Build());
}
_bsonTypes = [bsonType];
return this;
}
private record SchemaMetadata(Guid? KeyId, CsfleEncryptionAlgorithm? Algorithm)
/// <summary>
///
/// </summary>
/// <param name="bsonTypes"></param>
/// <returns></returns>
public PropertyBuilder<TDocument> WithBsonTypes(IEnumerable<BsonType> bsonTypes)
{
public BsonDocument Build() => new("encryptMetadata", GetEncryptBsonDocument(KeyId, Algorithm, null));
_bsonTypes = [..bsonTypes];
return this;
}
private static BsonDocument GetEncryptBsonDocument(Guid? keyId, CsfleEncryptionAlgorithm? algorithm, BsonType? bsonType)
internal BsonDocument Build(RenderArgs<TDocument> args)
{
return new BsonDocument
{
{ "bsonType", () => MapBsonTypeToString(bsonType!.Value), bsonType is not null },
{ "algorithm", () => MapCsfleEncyptionAlgorithmToString(algorithm!.Value), algorithm is not null },
{
"keyId",
() => new BsonArray(new[] { new BsonBinaryData(keyId!.Value, GuidRepresentation.Standard) }),
keyId is not null
},
};
return new BsonDocument(_path.Render(args).FieldName, new BsonDocument("encrypt", GetEncryptBsonDocument(_keyId, _algorithm, _bsonTypes)));
}
}
public class ElementBuilder<TSelf> where TSelf : ElementBuilder<TSelf>
/// <summary>
///
/// </summary>
/// <typeparam name="TDocument"></typeparam>
public class PatternPropertyBuilder<TDocument> : ElementBuilder<PropertyBuilder<TDocument>>
{
private CsfleEncryptionAlgorithm _algorithm;
private Guid _keyId;
private readonly string _pattern;
private List<BsonType> _bsonTypes;
public TSelf WithKeyId(Guid keyId)
/// <summary>
///
/// </summary>
/// <param name="pattern"></param>
public PatternPropertyBuilder(string pattern)
{
_keyId = keyId;
return (TSelf)this;
_pattern = pattern;
}
public TSelf WithAlgorithm(CsfleEncryptionAlgorithm algorithm)
/// <summary>
///
/// </summary>
/// <param name="bsonType"></param>
/// <returns></returns>
public PatternPropertyBuilder<TDocument> WithBsonType(BsonType bsonType)
{
_algorithm = algorithm;
return (TSelf)this;
_bsonTypes = [bsonType];
return this;
}
}
public class EncryptMetadataBuilder : ElementBuilder<EncryptMetadataBuilder>
{
/// <summary>
///
/// </summary>
/// <param name="bsonTypes"></param>
/// <returns></returns>
public PatternPropertyBuilder<TDocument> WithBsonTypes(IEnumerable<BsonType> bsonTypes)
{
_bsonTypes = [..bsonTypes];
return this;
}
internal BsonDocument Build(RenderArgs<TDocument> args)
{
return new BsonDocument(_pattern, new BsonDocument("encrypt", GetEncryptBsonDocument(_keyId, _algorithm, _bsonTypes)));
}
}
public abstract class PropertyBuilder: ElementBuilder<PropertyBuilder<PropertyBuilder>>
/// <summary>
///
/// </summary>
public abstract class NestedPropertyBuilder<TDocument>
{
internal abstract BsonDocument Build(RenderArgs<TDocument> args);
}
public class PropertyBuilder<TDocument> : PropertyBuilder
/// <summary>
///
/// </summary>
/// <typeparam name="TDocument"></typeparam>
/// <typeparam name="TField"></typeparam>
public class NestedPropertyBuilder<TDocument, TField> : NestedPropertyBuilder<TDocument>
{
private readonly FieldDefinition<TDocument> _path;
private List<BsonType> _bsonTypes;
private readonly Action<TypedBuilder<TField>> _configure;
public PropertyBuilder(FieldDefinition<TDocument> path)
/// <summary>
///
/// </summary>
/// <param name="path"></param>
/// <param name="configure"></param>
public NestedPropertyBuilder(FieldDefinition<TDocument> path, Action<TypedBuilder<TField>> configure)
{
_path = path;
_configure = configure;
}
public PropertyBuilder<TDocument> WithBsonType(BsonType bsonType)
{
_bsonTypes = [bsonType];
return this;
}
public PropertyBuilder<TDocument> WithBsonTypes(IEnumerable<BsonType> bsonTypes)
internal override BsonDocument Build(RenderArgs<TDocument> args)
{
_bsonTypes = [..bsonTypes];
return this;
var fieldBuilder = new TypedBuilder<TField>();
_configure(fieldBuilder);
return new BsonDocument(_path.Render(args).FieldName, fieldBuilder.Build());
}
}
public abstract class NestedDocumentBuilder: ElementBuilder<NestedDocumentBuilder>
/// <summary>
///
/// </summary>
public abstract class NestedPatternPropertyBuilder<TDocument>
{
internal abstract BsonDocument Build(RenderArgs<TDocument> args);
}
public class NestedDocumentBuilder<TDocument, TField> : NestedDocumentBuilder
/// <summary>
///
/// </summary>
/// <typeparam name="TDocument"></typeparam>
/// <typeparam name="TField"></typeparam>
public class NestedPatternPropertyBuilder<TDocument, TField> : NestedPatternPropertyBuilder<TDocument>
{
private readonly FieldDefinition<TDocument> _path;
private readonly Action<TypedBuilder<TField>> _configure;
public NestedDocumentBuilder(FieldDefinition<TDocument> path, Action<TypedBuilder<TField>> configure)
/// <summary>
///
/// </summary>
/// <param name="path"></param>
/// <param name="configure"></param>
public NestedPatternPropertyBuilder(FieldDefinition<TDocument> path, Action<TypedBuilder<TField>> configure)
{
_path = path;
_configure = configure;
}
internal override BsonDocument Build(RenderArgs<TDocument> args)
{
var fieldBuilder = new TypedBuilder<TField>();
_configure(fieldBuilder);
return new BsonDocument(_path.Render(args).FieldName, fieldBuilder.Build());
}
}
public class TypedBuilder<TDocument>
/// <summary>
///
/// </summary>
public abstract class TypedBuilder
{
private readonly List<NestedDocumentBuilder> _nestedDocument = [];
private readonly List<PropertyBuilder> _properties = [];
internal abstract BsonDocument Build();
}
/// <summary>
///
/// </summary>
/// <typeparam name="TDocument"></typeparam>
public class TypedBuilder<TDocument> : TypedBuilder
{
private readonly List<NestedPropertyBuilder<TDocument>> _nestedProperties = [];
private readonly List<PropertyBuilder<TDocument>> _properties = [];
private EncryptMetadataBuilder _metadata;
/// <summary>
///
/// </summary>
/// <returns></returns>
public EncryptMetadataBuilder EncryptMetadata()
{
_metadata = new EncryptMetadataBuilder();
return _metadata;
}
public PropertyBuilder Property(FieldDefinition<TDocument> path)
/// <summary>
///
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public PropertyBuilder<TDocument> Property(FieldDefinition<TDocument> path)
{
var property = new PropertyBuilder<TDocument>(path);
_properties.Add(property);
return property;
}
/// <summary>
///
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public PropertyBuilder<TDocument> Property<TField>(Expression<Func<TDocument, TField>> path)
{
return Property(new ExpressionFieldDefinition<TDocument, TField>(path));
}
/// <summary>
///
/// </summary>
/// <param name="path"></param>
/// <param name="configure"></param>
/// <returns></returns>
public NestedPropertyBuilder<TDocument, TField> NestedProperty<TField>(FieldDefinition<TDocument> path, Action<TypedBuilder<TField>> configure)
{
var nestedProperty = new NestedPropertyBuilder<TDocument,TField>(path, configure);
_nestedProperties.Add(nestedProperty);
return nestedProperty;
}
/// <summary>
///
/// </summary>
/// <param name="path"></param>
/// <param name="configure"></param>
/// <returns></returns>
public NestedPropertyBuilder<TDocument, TField> NestedProperty<TField>(Expression<Func<TDocument, TField>> path, Action<TypedBuilder<TField>> configure)
{
return NestedProperty(new ExpressionFieldDefinition<TDocument, TField>(path), configure);
}
internal override BsonDocument Build()
{
var schema = new BsonDocument("bsonType", "object");
if (_metadata is not null)
{
schema.Merge(_metadata.Build());
}
var args = new RenderArgs<TDocument>(BsonSerializer.LookupSerializer<TDocument>(), BsonSerializer.SerializerRegistry);
BsonDocument properties = null;
if (_nestedProperties.Any())
{
properties = new BsonDocument();
foreach (var nestedProperty in _nestedProperties)
{
properties.Merge(nestedProperty.Build(args));
}
}
if (_properties.Any())
{
properties ??= new BsonDocument();
foreach (var property in _properties)
{
properties.Merge(property.Build(args));
}
}
if (properties != null)
{
schema.Add("properties", properties);
}
return schema;
}
}
/// <summary>

595
tests/MongoDB.Driver.Tests/Encryption/CsfleSchemaBuilderTests.cs

@ -27,444 +27,30 @@ namespace MongoDB.Driver.Tests.Encryption
private readonly Guid _keyIdExample = Guid.Parse("6f4af470-00d1-401f-ac39-f45902a0c0c8");
[Fact]
public void TypedSchemaBuilder_Property_throws_when_path_is_null()
{
var exception = Record.Exception(() => new CsfleTypeSchemaBuilder<BsonDocument>().Property(null));
Assert.NotNull(exception);
Assert.IsType<ArgumentNullException>(exception);
}
[Fact]
public void TypedSchemaBuilder_PropertyWithConfigure_throws_when_path_is_null()
{
Action<CsfleTypeSchemaBuilder<BsonDocument>> configure = b => { };
var exception = Record.Exception(() => new CsfleTypeSchemaBuilder<BsonDocument>().Property((FieldDefinition<BsonDocument>)null, configure));
Assert.NotNull(exception);
Assert.IsType<ArgumentNullException>(exception);
}
[Fact]
public void TypedSchemaBuilder_PropertyWithConfigure_throws_when_configure_is_null()
{
Action<CsfleTypeSchemaBuilder<BsonDocument>> configure = null;
var exception = Record.Exception(() => new CsfleTypeSchemaBuilder<BsonDocument>().Property("path", configure));
Assert.NotNull(exception);
Assert.IsType<ArgumentNullException>(exception);
}
[Fact]
public void TypedSchemaBuilder_PatternProperty_throws_when_pattern_is_null()
{
var exception = Record.Exception(() => new CsfleTypeSchemaBuilder<BsonDocument>().PatternProperty(null));
Assert.NotNull(exception);
Assert.IsType<ArgumentException>(exception);
}
[Fact]
public void TypedSchemaBuilder_PatternPropertyWithConfigure_throws_when_pattern_is_null()
{
Action<CsfleTypeSchemaBuilder<BsonDocument>> configure = b => { };
var exception = Record.Exception(() => new CsfleTypeSchemaBuilder<BsonDocument>().PatternProperty((FieldDefinition<BsonDocument>)null, configure));
Assert.NotNull(exception);
Assert.IsType<ArgumentNullException>(exception);
}
[Fact]
public void TypedSchemaBuilder_PatternPropertyWithConfigure_throws_when_configure_is_null()
{
Action<CsfleTypeSchemaBuilder<BsonDocument>> configure = null;
var exception = Record.Exception(() => new CsfleTypeSchemaBuilder<BsonDocument>().PatternProperty("path", configure));
Assert.NotNull(exception);
Assert.IsType<ArgumentNullException>(exception);
}
[Fact]
public void TypedSchemaBuilder_Property_works_as_expected()
{
var builder = new CsfleTypeSchemaBuilder<Patient>()
.Property("bloodType", keyId: _keyIdExample, bsonType: BsonType.String,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random);
var expected = """
{
"bsonType": "object",
"properties": {
"bloodType": {
"encrypt": {
"bsonType": "string",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
}
}
}
}
""";
AssertOutcomeTypeBuilder(builder, expected);
}
[Fact]
public void TypedSchemaBuilder_PropertyWithExpression_works_as_expected()
{
var builder = new CsfleTypeSchemaBuilder<Patient>()
.Property(p => p.BloodType, keyId: _keyIdExample, bsonType: BsonType.String,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random);
var expected = """
{
"bsonType": "object",
"properties": {
"bloodType": {
"encrypt": {
"bsonType": "string",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
}
}
}
}
""";
AssertOutcomeTypeBuilder(builder, expected);
}
[Fact]
public void TypedSchemaBuilder_PropertyWithConfigure_works_as_expected()
{
var builder = new CsfleTypeSchemaBuilder<Patient>()
.Property<Insurance>("insurance", insurance => insurance
.Property("policyNumber", bsonType: BsonType.Int32,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic,
keyId: _keyIdExample));
var expected = """
{
"bsonType": "object",
"properties": {
"insurance": {
"bsonType": "object",
"properties": {
"policyNumber": {
"encrypt": {
"bsonType": "int",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
}
}
}
}
}
}
""";
AssertOutcomeTypeBuilder(builder, expected);
}
[Fact]
public void TypedSchemaBuilder_PropertyWithExpressionAndConfigure_works_as_expected()
{
var builder = new CsfleTypeSchemaBuilder<Patient>()
.Property(p => p.Insurance, insurance => insurance
.Property("policyNumber", bsonType: BsonType.Int32,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic,
keyId: _keyIdExample));
var expected = """
{
"bsonType": "object",
"properties": {
"insurance": {
"bsonType": "object",
"properties": {
"policyNumber": {
"encrypt": {
"bsonType": "int",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
}
}
}
}
}
}
""";
AssertOutcomeTypeBuilder(builder, expected);
}
[Fact]
public void TypedSchemaBuilder_PatternProperty_works_as_expected()
{
var builder = new CsfleTypeSchemaBuilder<Patient>()
.PatternProperty("_PIIString$", bsonType: BsonType.String,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic,
keyId: _keyIdExample);
var expected = """
{
"bsonType": "object",
"patternProperties": {
"_PIIString$": {
"encrypt": {
"bsonType": "string",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
}
}
}
}
""";
AssertOutcomeTypeBuilder(builder, expected);
}
[Fact]
public void TypedSchemaBuilder_PatternPropertyWithConfigure_works_as_expected()
{
var builder = new CsfleTypeSchemaBuilder<Patient>()
.PatternProperty<Insurance>("insurance", builder => builder
.PatternProperty("_PIINumber$", bsonType: BsonType.Int32,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic));
var expected = """
{
"bsonType": "object",
"patternProperties": {
"insurance": {
"bsonType": "object",
"patternProperties": {
"_PIINumber$": {
"encrypt": {
"bsonType": "int",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
},
},
},
}
}
}
""";
AssertOutcomeTypeBuilder(builder, expected);
}
[Fact]
public void TypedSchemaBuilder_PatternPropertyWithExpressionAndConfigure_works_as_expected()
{
var builder = new CsfleTypeSchemaBuilder<Patient>()
.PatternProperty(p=> p.Insurance, builder => builder
.PatternProperty("_PIINumber$", bsonType: BsonType.Int32,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic));
var expected = """
{
"bsonType": "object",
"patternProperties": {
"insurance": {
"bsonType": "object",
"patternProperties": {
"_PIINumber$": {
"encrypt": {
"bsonType": "int",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
},
},
},
}
}
}
""";
AssertOutcomeTypeBuilder(builder, expected);
}
[Fact]
public void TypedSchemaBuilder_EncryptMetadata_works_as_expected()
{
var builder = new CsfleTypeSchemaBuilder<Patient>()
.EncryptMetadata(keyId: _keyIdExample,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic);
var expected = """
{
"bsonType": "object",
"encryptMetadata": {
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
},
}
""";
AssertOutcomeTypeBuilder(builder, expected);
}
[Fact]
public void SchemaBuilder_WithType_works_as_expected()
{
const string collectionName1 = "medicalRecords.patients";
const string collectionName2 = "test.collection";
var typedBuilder1 = new CsfleTypeSchemaBuilder<Patient>()
.EncryptMetadata(keyId: _keyIdExample,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic);
var typeBuilder2 = new CsfleTypeSchemaBuilder<BsonDocument>()
.EncryptMetadata(algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random);
var encryptionSchemaBuilder = new CsfleSchemaBuilder()
.WithType(CollectionNamespace.FromFullName(collectionName1), typedBuilder1)
.WithType(CollectionNamespace.FromFullName(collectionName2), typeBuilder2);
var expected = new Dictionary<string, string>
{
[collectionName1] = """
{
"bsonType": "object",
"encryptMetadata": {
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
},
}
""",
[collectionName2] = """
{
"bsonType": "object",
"encryptMetadata": {
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random",
},
}
"""
};
AssertOutcomeBuilder(encryptionSchemaBuilder, expected);
}
[Fact]
public void SchemaBuilder_WithTypeAndConfigure_works_as_expected()
{
const string collectionName1 = "medicalRecords.patients";
const string collectionName2 = "test.collection";
var encryptionSchemaBuilder = new CsfleSchemaBuilder()
.WithType<Patient>(CollectionNamespace.FromFullName(collectionName1), b => b.EncryptMetadata(keyId: _keyIdExample, algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic))
.WithType<BsonDocument>(CollectionNamespace.FromFullName(collectionName2), b => b.EncryptMetadata(algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random));
var expected = new Dictionary<string, string>
{
[collectionName1] = """
{
"bsonType": "object",
"encryptMetadata": {
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
},
}
""",
[collectionName2] = """
{
"bsonType": "object",
"encryptMetadata": {
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random",
},
}
"""
};
AssertOutcomeBuilder(encryptionSchemaBuilder, expected);
}
[Fact]
public void SchemaBuilder_CompleteExampleWithBsonDocument_work_as_expected()
{
var collectionName = "medicalRecords.patients";
var typedBuilder = CsfleSchemaBuilder.GetTypeBuilder<BsonDocument>()
.EncryptMetadata(keyId: _keyIdExample)
.Property<BsonDocument>("insurance", insurance => insurance
.Property("policyNumber", bsonType: BsonType.Int32,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic))
.Property("medicalRecords", bsonType: BsonType.Array,
algorithm: CsfleEncryptionAlgorithm
.AEAD_AES_256_CBC_HMAC_SHA_512_Random)
.Property("bloodType", bsonType: BsonType.String,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random)
.Property("ssn", bsonType: BsonType.Int32,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic);
var encryptionSchemaBuilder = new CsfleSchemaBuilder()
.WithType(CollectionNamespace.FromFullName(collectionName), typedBuilder);
var expected = new Dictionary<string, string>
{
[collectionName] = """
{
"bsonType": "object",
"encryptMetadata": {
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
},
"properties": {
"insurance": {
"bsonType": "object",
"properties": {
"policyNumber": {
"encrypt": {
"bsonType": "int",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
}
}
}
},
"medicalRecords": {
"encrypt": {
"bsonType": "array",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
}
},
"bloodType": {
"encrypt": {
"bsonType": "string",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
}
},
"ssn": {
"encrypt": {
"bsonType": "int",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
}
}
},
}
"""
};
AssertOutcomeBuilder(encryptionSchemaBuilder, expected);
}
[Fact]
public void SchemaBuilder_CompleteExample_work_as_expected()
public void BasicCompleteTest()
{
const string collectionName = "medicalRecords.patients";
var typedBuilder = CsfleSchemaBuilder.GetTypeBuilder<Patient>()
.EncryptMetadata(keyId: _keyIdExample)
.Property(p => p.Insurance, insurance => insurance
.Property(i => i.PolicyNumber, bsonType: BsonType.Int32,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic))
.Property(p => p.MedicalRecords, bsonType: BsonType.Array,
algorithm: CsfleEncryptionAlgorithm
.AEAD_AES_256_CBC_HMAC_SHA_512_Random)
.Property("bloodType", bsonType: BsonType.String,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random)
.Property(p => p.Ssn, bsonType: BsonType.Int32,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic);
var encryptionSchemaBuilder = new CsfleSchemaBuilder()
.WithType(CollectionNamespace.FromFullName(collectionName), typedBuilder);
var builder = CsfleSchemaBuilder.Create(schemaBuilder =>
{
schemaBuilder.Encrypt<Patient>(CollectionNamespace.FromFullName(collectionName), builder1 =>
{
builder1.EncryptMetadata().WithKeyId(_keyIdExample);
builder1.NestedProperty(p => p.Insurance, typedBuilder1 =>
{
typedBuilder1.Property(i => i.PolicyNumber).WithBsonType(BsonType.Int32)
.WithAlgorithm(CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic);
});
builder1.Property(p => p.MedicalRecords).WithBsonType(BsonType.Array)
.WithAlgorithm(CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random);
builder1.Property("bloodType").WithBsonType(BsonType.String)
.WithAlgorithm(CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random);
builder1.Property(p => p.Ssn).WithBsonType(BsonType.Int32)
.WithAlgorithm(CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic);
} );
});
var expected = new Dictionary<string, string>
{
@ -509,79 +95,71 @@ namespace MongoDB.Driver.Tests.Encryption
"""
};
AssertOutcomeBuilder(encryptionSchemaBuilder, expected);
}
[Fact]
public void SchemaBuilder_CompleteExampleWithPatternProperties_work_as_expected()
{
const string collectionName = "medicalRecords.patients";
var typedBuilder = CsfleSchemaBuilder.GetTypeBuilder<Patient>()
.PatternProperty("_PIIString$", bsonType: BsonType.String,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
.PatternProperty("_PIIArray$", bsonType: BsonType.Array,
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random)
.PatternProperty(p => p.Insurance, builder => builder
.PatternProperty("_PIINumber$", bsonType: BsonType.Int32, algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
.PatternProperty("_PIIString$", bsonType: BsonType.String, algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
);
var encryptionSchemaBuilder = new CsfleSchemaBuilder()
.WithType(CollectionNamespace.FromFullName(collectionName), typedBuilder);
var expected = new Dictionary<string, string>
{
[collectionName] = """
{
"bsonType": "object",
"patternProperties": {
"_PIIString$": {
"encrypt": {
"bsonType": "string",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
},
},
"_PIIArray$": {
"encrypt": {
"bsonType": "array",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random",
},
},
"insurance": {
"bsonType": "object",
"patternProperties": {
"_PIINumber$": {
"encrypt": {
"bsonType": "int",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
},
},
"_PIIString$": {
"encrypt": {
"bsonType": "string",
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
},
},
},
},
},
}
"""
};
AssertOutcomeBuilder(encryptionSchemaBuilder, expected);
}
private void AssertOutcomeTypeBuilder(CsfleTypeSchemaBuilder builder, string expected)
{
var parsedExpected = BsonDocument.Parse(expected);
var builtSchema = builder.Build();
Assert.Equal(parsedExpected, builtSchema);
}
private void AssertOutcomeBuilder(CsfleSchemaBuilder builder, Dictionary<string, string> expected)
AssertOutcomeBuilder2(builder, expected);
}
// [Fact]
// public void BasicPatternTest()
// {
// const string collectionName = "medicalRecords.patients";
//
// var typedBuilder = CsfleSchemaBuilder.GetTypeBuilder<Patient>()
// .PatternProperty("_PIIString$", bsonType: BsonType.String,
// algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
// .PatternProperty("_PIIArray$", bsonType: BsonType.Array,
// algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random)
// .PatternProperty(p => p.Insurance, builder => builder
// .PatternProperty("_PIINumber$", bsonType: BsonType.Int32, algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
// .PatternProperty("_PIIString$", bsonType: BsonType.String, algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
// );
//
// var encryptionSchemaBuilder = new CsfleSchemaBuilder()
// .WithType(CollectionNamespace.FromFullName(collectionName), typedBuilder);
//
// var expected = new Dictionary<string, string>
// {
// [collectionName] = """
// {
// "bsonType": "object",
// "patternProperties": {
// "_PIIString$": {
// "encrypt": {
// "bsonType": "string",
// "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
// },
// },
// "_PIIArray$": {
// "encrypt": {
// "bsonType": "array",
// "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random",
// },
// },
// "insurance": {
// "bsonType": "object",
// "patternProperties": {
// "_PIINumber$": {
// "encrypt": {
// "bsonType": "int",
// "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
// },
// },
// "_PIIString$": {
// "encrypt": {
// "bsonType": "string",
// "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
// },
// },
// },
// },
// },
// }
// """
// };
//
// AssertOutcomeBuilder(encryptionSchemaBuilder, expected);
// }
private void AssertOutcomeBuilder2(CsfleSchemaBuilder builder, Dictionary<string, string> expected)
{
var builtSchema = builder.Build();
Assert.Equal(expected.Count, builtSchema.Count);
@ -592,6 +170,7 @@ namespace MongoDB.Driver.Tests.Encryption
}
}
internal class Patient
{
[BsonId]

Loading…
Cancel
Save