You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
710 lines
32 KiB
710 lines
32 KiB
/* Copyright 2010-present MongoDB Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using FluentAssertions;
|
|
using MongoDB.Bson;
|
|
using MongoDB.Bson.Serialization.Attributes;
|
|
using MongoDB.Driver.Encryption;
|
|
using Xunit;
|
|
|
|
namespace MongoDB.Driver.Tests.Encryption
|
|
{
|
|
public class CsfleSchemaBuilderTests
|
|
{
|
|
private readonly CollectionNamespace _collectionNamespace = CollectionNamespace.FromFullName("medicalRecords.patients");
|
|
private const string _keyIdString = "6f4af470-00d1-401f-ac39-f45902a0c0c8";
|
|
private static Guid _keyId = Guid.Parse(_keyIdString);
|
|
|
|
[Fact]
|
|
public void CsfleSchemaBuilder_works_as_expected()
|
|
{
|
|
var builder = CsfleSchemaBuilder.Create(schemaBuilder =>
|
|
{
|
|
schemaBuilder.Encrypt<Patient>(_collectionNamespace, builder =>
|
|
{
|
|
builder
|
|
.EncryptMetadata(keyId: _keyId)
|
|
.Property(p => p.MedicalRecords, BsonType.Array,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random)
|
|
.Property("bloodType", BsonType.String,
|
|
algorithm: EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random)
|
|
.Property(p => p.Ssn, BsonType.Int32,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
|
|
.Property(p => p.Insurance, innerBuilder =>
|
|
{
|
|
innerBuilder
|
|
.Property(i => i.PolicyNumber, BsonType.Int32,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic);
|
|
})
|
|
.PatternProperty("_PIIString$", BsonType.String, EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
|
|
.PatternProperty("_PIIArray$", BsonType.Array, EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random)
|
|
.PatternProperty(p => p.Insurance, innerBuilder =>
|
|
{
|
|
innerBuilder
|
|
.PatternProperty("_PIIString$", BsonType.String,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
|
|
.PatternProperty("_PIINumber$", BsonType.Int32,
|
|
algorithm: EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic);
|
|
});
|
|
|
|
} );
|
|
});
|
|
|
|
var expected = new Dictionary<string, string>
|
|
{
|
|
[_collectionNamespace.FullName] = """
|
|
{
|
|
"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"
|
|
}
|
|
}
|
|
},
|
|
"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",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
"""
|
|
};
|
|
|
|
AssertOutcomeCsfleSchemaBuilder(builder, expected);
|
|
}
|
|
|
|
[Fact]
|
|
public void CsfleSchemaBuilder_with_multiple_types_works_as_expected()
|
|
{
|
|
var testCollectionNamespace = CollectionNamespace.FromFullName("test.class");
|
|
|
|
var builder = CsfleSchemaBuilder.Create(schemaBuilder =>
|
|
{
|
|
schemaBuilder.Encrypt<Patient>(_collectionNamespace, builder =>
|
|
{
|
|
builder
|
|
.EncryptMetadata(keyId: _keyId)
|
|
.Property(p => p.MedicalRecords, BsonType.Array,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random);
|
|
});
|
|
|
|
schemaBuilder.Encrypt<TestClass>(testCollectionNamespace, builder =>
|
|
{
|
|
builder.Property(t => t.TestString, BsonType.String);
|
|
});
|
|
});
|
|
|
|
var expected = new Dictionary<string, string>
|
|
{
|
|
[_collectionNamespace.FullName] = """
|
|
{
|
|
"bsonType": "object",
|
|
"encryptMetadata": {
|
|
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
|
|
},
|
|
"properties": {
|
|
"medicalRecords": {
|
|
"encrypt": {
|
|
"bsonType": "array",
|
|
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
|
|
}
|
|
},
|
|
},
|
|
}
|
|
""",
|
|
[testCollectionNamespace.FullName] = """
|
|
{
|
|
"bsonType": "object",
|
|
"properties": {
|
|
"TestString": {
|
|
"encrypt": {
|
|
"bsonType": "string",
|
|
}
|
|
},
|
|
}
|
|
}
|
|
"""
|
|
};
|
|
|
|
AssertOutcomeCsfleSchemaBuilder(builder, expected);
|
|
}
|
|
|
|
[Fact]
|
|
public void CsfleSchemaBuilder_with_no_schemas_throws()
|
|
{
|
|
var builder = CsfleSchemaBuilder.Create(_ =>
|
|
{
|
|
// No schemas added
|
|
});
|
|
|
|
var exception = Record.Exception(() => builder.Build());
|
|
|
|
exception.Should().NotBeNull();
|
|
exception.Should().BeOfType<InvalidOperationException>();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random,
|
|
null,
|
|
""" "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" """)]
|
|
[InlineData(
|
|
null,
|
|
_keyIdString,
|
|
""" "keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }] """)]
|
|
public void EncryptedCollection_Metadata_works_as_expected(EncryptionAlgorithm? algorithm, string keyString, string expectedContent)
|
|
{
|
|
Guid? keyId = keyString is null ? null : Guid.Parse(keyString);
|
|
var builder = new EncryptedCollectionBuilder<Patient>();
|
|
|
|
builder.EncryptMetadata(keyId, algorithm);
|
|
|
|
var expected = $$"""
|
|
{
|
|
"bsonType": "object",
|
|
"encryptMetadata": {
|
|
{{expectedContent}}
|
|
}
|
|
}
|
|
""";
|
|
|
|
AssertOutcomeCollectionBuilder(builder, expected);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(BsonType.Array,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random,
|
|
null,
|
|
""" "bsonType": "array", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" """)]
|
|
[InlineData(BsonType.Array,
|
|
null,
|
|
_keyIdString,
|
|
""" "bsonType": "array", "keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }] """)]
|
|
[InlineData(BsonType.Array,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random,
|
|
_keyIdString,
|
|
""" "bsonType": "array", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", "keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }] """)]
|
|
public void EncryptedCollection_PatternProperty_works_as_expected(BsonType bsonType, EncryptionAlgorithm? algorithm, string keyString, string expectedContent)
|
|
{
|
|
Guid? keyId = keyString is null ? null : Guid.Parse(keyString);
|
|
var builder = new EncryptedCollectionBuilder<Patient>();
|
|
|
|
builder.PatternProperty("randomRegex*", bsonType, algorithm, keyId);
|
|
|
|
var expected = $$"""
|
|
{
|
|
"bsonType": "object",
|
|
"patternProperties": {
|
|
"randomRegex*": {
|
|
"encrypt": {
|
|
{{expectedContent}}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
""";
|
|
|
|
AssertOutcomeCollectionBuilder(builder, expected);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null,
|
|
null,
|
|
null,
|
|
"")]
|
|
[InlineData(new[] {BsonType.Array, BsonType.String},
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random,
|
|
null,
|
|
""" "bsonType": ["array", "string"], "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" """)]
|
|
[InlineData(new[] {BsonType.Array, BsonType.String},
|
|
null,
|
|
_keyIdString,
|
|
""" "bsonType": ["array", "string"], "keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }] """)]
|
|
[InlineData(new[] {BsonType.Array, BsonType.String},
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random,
|
|
_keyIdString,
|
|
""" "bsonType": ["array", "string"], "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", "keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }] """)]
|
|
public void EncryptedCollection_PatternProperty_with_multiple_bson_types_works_as_expected(IEnumerable<BsonType> bsonTypes, EncryptionAlgorithm? algorithm, string keyString, string expectedContent)
|
|
{
|
|
Guid? keyId = keyString is null ? null : Guid.Parse(keyString);
|
|
var builder = new EncryptedCollectionBuilder<Patient>();
|
|
|
|
builder.PatternProperty("randomRegex*", bsonTypes, algorithm, keyId);
|
|
|
|
var expected = $$"""
|
|
{
|
|
"bsonType": "object",
|
|
"patternProperties": {
|
|
"randomRegex*": {
|
|
"encrypt": {
|
|
{{expectedContent}}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
""";
|
|
|
|
AssertOutcomeCollectionBuilder(builder, expected);
|
|
}
|
|
|
|
[Fact]
|
|
public void EncryptedCollection_PatternProperty_nested_works_as_expected()
|
|
{
|
|
Guid? keyId = Guid.Parse(_keyIdString);
|
|
var builder = new EncryptedCollectionBuilder<Patient>();
|
|
|
|
builder.PatternProperty(p => p.Insurance, innerBuilder =>
|
|
{
|
|
innerBuilder
|
|
.EncryptMetadata(keyId)
|
|
.Property("policyNumber", BsonType.Int32,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
|
|
.PatternProperty("randomRegex*", BsonType.String,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random);
|
|
});
|
|
|
|
var expected = """
|
|
{
|
|
"bsonType": "object",
|
|
"patternProperties": {
|
|
"insurance": {
|
|
"bsonType": "object",
|
|
"encryptMetadata": {
|
|
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
|
|
},
|
|
"properties": {
|
|
"policyNumber": {
|
|
"encrypt": {
|
|
"bsonType": "int",
|
|
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
|
|
}
|
|
}
|
|
},
|
|
"patternProperties": {
|
|
"randomRegex*": {
|
|
"encrypt": {
|
|
"bsonType": "string",
|
|
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
""";
|
|
|
|
AssertOutcomeCollectionBuilder(builder, expected);
|
|
}
|
|
|
|
[Fact]
|
|
public void EncryptedCollection_PatternProperty_nested_with_string_works_as_expected()
|
|
{
|
|
Guid? keyId = Guid.Parse(_keyIdString);
|
|
var builder = new EncryptedCollectionBuilder<Patient>();
|
|
|
|
builder.PatternProperty<Insurance>("insurance", innerBuilder =>
|
|
{
|
|
innerBuilder
|
|
.EncryptMetadata(keyId)
|
|
.Property("policyNumber", BsonType.Int32,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
|
|
.PatternProperty("randomRegex*", BsonType.String,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random);
|
|
});
|
|
|
|
var expected = """
|
|
{
|
|
"bsonType": "object",
|
|
"patternProperties": {
|
|
"insurance": {
|
|
"bsonType": "object",
|
|
"encryptMetadata": {
|
|
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
|
|
},
|
|
"properties": {
|
|
"policyNumber": {
|
|
"encrypt": {
|
|
"bsonType": "int",
|
|
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
|
|
}
|
|
}
|
|
},
|
|
"patternProperties": {
|
|
"randomRegex*": {
|
|
"encrypt": {
|
|
"bsonType": "string",
|
|
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
""";
|
|
|
|
AssertOutcomeCollectionBuilder(builder, expected);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(BsonType.Array,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random,
|
|
null,
|
|
""" "bsonType": "array", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" """)]
|
|
[InlineData(BsonType.Array,
|
|
null,
|
|
_keyIdString,
|
|
""" "bsonType": "array", "keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }] """)]
|
|
[InlineData(BsonType.Array,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random,
|
|
_keyIdString,
|
|
""" "bsonType": "array", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", "keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }] """)]
|
|
public void EncryptedCollection_Property_with_expression_works_as_expected(BsonType bsonType, EncryptionAlgorithm? algorithm, string keyString, string expectedContent)
|
|
{
|
|
Guid? keyId = keyString is null ? null : Guid.Parse(keyString);
|
|
var builder = new EncryptedCollectionBuilder<Patient>();
|
|
|
|
builder.Property(p => p.MedicalRecords, bsonType, algorithm, keyId);
|
|
|
|
var expected = $$"""
|
|
{
|
|
"bsonType": "object",
|
|
"properties": {
|
|
"medicalRecords": {
|
|
"encrypt": {
|
|
{{expectedContent}}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
""";
|
|
|
|
AssertOutcomeCollectionBuilder(builder, expected);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null,
|
|
null,
|
|
null,
|
|
"")]
|
|
[InlineData(new[] {BsonType.Array, BsonType.String},
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random,
|
|
null,
|
|
""" "bsonType": ["array", "string"], "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" """)]
|
|
[InlineData(new[] {BsonType.Array, BsonType.String},
|
|
null,
|
|
_keyIdString,
|
|
""" "bsonType": ["array", "string"], "keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }] """)]
|
|
[InlineData(new[] {BsonType.Array, BsonType.String},
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random,
|
|
_keyIdString,
|
|
""" "bsonType": ["array", "string"], "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", "keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }] """)]
|
|
public void EncryptedCollection_Property_with_multiple_bson_types_works_as_expected(IEnumerable<BsonType> bsonTypes, EncryptionAlgorithm? algorithm, string keyString, string expectedContent)
|
|
{
|
|
Guid? keyId = keyString is null ? null : Guid.Parse(keyString);
|
|
var builder = new EncryptedCollectionBuilder<Patient>();
|
|
|
|
builder.Property(p => p.MedicalRecords, bsonTypes, algorithm, keyId);
|
|
|
|
var expected = $$"""
|
|
{
|
|
"bsonType": "object",
|
|
"properties": {
|
|
"medicalRecords": {
|
|
"encrypt": {
|
|
{{expectedContent}}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
""";
|
|
|
|
AssertOutcomeCollectionBuilder(builder, expected);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(BsonType.Array,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random,
|
|
null,
|
|
""" "bsonType": "array", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" """)]
|
|
[InlineData(BsonType.Array,
|
|
null,
|
|
_keyIdString,
|
|
""" "bsonType": "array", "keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }] """)]
|
|
[InlineData(BsonType.Array,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random,
|
|
_keyIdString,
|
|
""" "bsonType": "array", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", "keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }] """)]
|
|
public void EncryptedCollection_Property_with_string_works_as_expected(BsonType bsonType, EncryptionAlgorithm? algorithm, string keyString, string expectedContent)
|
|
{
|
|
Guid? keyId = keyString is null ? null : Guid.Parse(keyString);
|
|
var builder = new EncryptedCollectionBuilder<Patient>();
|
|
|
|
builder.Property("medicalRecords", bsonType, algorithm, keyId);
|
|
|
|
var expected = $$"""
|
|
{
|
|
"bsonType": "object",
|
|
"properties": {
|
|
"medicalRecords": {
|
|
"encrypt": {
|
|
{{expectedContent}}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
""";
|
|
|
|
AssertOutcomeCollectionBuilder(builder, expected);
|
|
}
|
|
|
|
[Fact]
|
|
public void EncryptedCollection_Property_nested_works_as_expected()
|
|
{
|
|
Guid? keyId = Guid.Parse(_keyIdString);
|
|
var builder = new EncryptedCollectionBuilder<Patient>();
|
|
|
|
builder.Property(p => p.Insurance, innerBuilder =>
|
|
{
|
|
innerBuilder
|
|
.EncryptMetadata(keyId)
|
|
.Property("policyNumber", BsonType.Int32,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
|
|
.PatternProperty("randomRegex*", BsonType.String,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random);
|
|
});
|
|
|
|
var expected = """
|
|
{
|
|
"bsonType": "object",
|
|
"properties": {
|
|
"insurance": {
|
|
"bsonType": "object",
|
|
"encryptMetadata": {
|
|
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
|
|
},
|
|
"properties": {
|
|
"policyNumber": {
|
|
"encrypt": {
|
|
"bsonType": "int",
|
|
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
|
|
}
|
|
}
|
|
},
|
|
"patternProperties": {
|
|
"randomRegex*": {
|
|
"encrypt": {
|
|
"bsonType": "string",
|
|
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
""";
|
|
|
|
AssertOutcomeCollectionBuilder(builder, expected);
|
|
}
|
|
|
|
[Fact]
|
|
public void EncryptedCollection_Property_nested_with_string_works_as_expected()
|
|
{
|
|
Guid? keyId = Guid.Parse(_keyIdString);
|
|
var builder = new EncryptedCollectionBuilder<Patient>();
|
|
|
|
builder.Property<Insurance>("insurance", innerBuilder =>
|
|
{
|
|
innerBuilder
|
|
.EncryptMetadata(keyId)
|
|
.Property("policyNumber", BsonType.Int32,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
|
|
.PatternProperty("randomRegex*", BsonType.String,
|
|
EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random);
|
|
});
|
|
|
|
var expected = """
|
|
{
|
|
"bsonType": "object",
|
|
"properties": {
|
|
"insurance": {
|
|
"bsonType": "object",
|
|
"encryptMetadata": {
|
|
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
|
|
},
|
|
"properties": {
|
|
"policyNumber": {
|
|
"encrypt": {
|
|
"bsonType": "int",
|
|
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
|
|
}
|
|
}
|
|
},
|
|
"patternProperties": {
|
|
"randomRegex*": {
|
|
"encrypt": {
|
|
"bsonType": "string",
|
|
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
""";
|
|
|
|
AssertOutcomeCollectionBuilder(builder, expected);
|
|
}
|
|
|
|
[Fact]
|
|
public void EncryptedCollection_Property_with_empty_bson_types_throws()
|
|
{
|
|
var builder = new EncryptedCollectionBuilder<Patient>();
|
|
|
|
var recordedException = Record.Exception(() => builder.Property("test", []));
|
|
recordedException.Should().NotBeNull();
|
|
recordedException.Should().BeOfType<ArgumentException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void EncryptedCollection_Metadata_with_empty_algorithm_and_key_throws()
|
|
{
|
|
var builder = new EncryptedCollectionBuilder<Patient>();
|
|
|
|
var recordedException = Record.Exception(() => builder.EncryptMetadata(null, null));
|
|
recordedException.Should().NotBeNull();
|
|
recordedException.Should().BeOfType<ArgumentException>();
|
|
}
|
|
|
|
private void AssertOutcomeCsfleSchemaBuilder(CsfleSchemaBuilder builder, Dictionary<string, string> expectedSchema)
|
|
{
|
|
var builtSchema = builder.Build();
|
|
expectedSchema.Should().HaveCount(builtSchema.Count);
|
|
foreach (var collectionNamespace in expectedSchema.Keys)
|
|
{
|
|
var parsed = BsonDocument.Parse(expectedSchema[collectionNamespace]);
|
|
builtSchema[collectionNamespace].Should().BeEquivalentTo(parsed);
|
|
}
|
|
}
|
|
|
|
private void AssertOutcomeCollectionBuilder<T>(EncryptedCollectionBuilder<T> builder, string expected)
|
|
{
|
|
var builtSchema = builder.Build();
|
|
var expectedSchema = BsonDocument.Parse(expected);
|
|
builtSchema.Should().BeEquivalentTo(expectedSchema);
|
|
}
|
|
|
|
internal class TestClass
|
|
{
|
|
public ObjectId Id { get; set; }
|
|
|
|
public string TestString { get; set; }
|
|
}
|
|
|
|
internal class Patient
|
|
{
|
|
[BsonId]
|
|
public ObjectId Id { get; set; }
|
|
|
|
[BsonElement("name")]
|
|
public string Name { get; set; }
|
|
|
|
[BsonElement("ssn")]
|
|
public int Ssn { get; set; }
|
|
|
|
[BsonElement("bloodType")]
|
|
public string BloodType { get; set; }
|
|
|
|
[BsonElement("medicalRecords")]
|
|
public List<MedicalRecord> MedicalRecords { get; set; }
|
|
|
|
[BsonElement("insurance")]
|
|
public Insurance Insurance { get; set; }
|
|
}
|
|
|
|
internal class MedicalRecord
|
|
{
|
|
[BsonElement("weight")]
|
|
public int Weight { get; set; }
|
|
|
|
[BsonElement("bloodPressure")]
|
|
public string BloodPressure { get; set; }
|
|
}
|
|
|
|
internal class Insurance
|
|
{
|
|
[BsonElement("provider")]
|
|
public string Provider { get; set; }
|
|
|
|
[BsonElement("policyNumber")]
|
|
public int PolicyNumber { get; set; }
|
|
}
|
|
}
|
|
}
|