Skip to content

Commit 0069760

Browse files
committed
Added exceptions and improved testing
1 parent f31c705 commit 0069760

File tree

2 files changed

+143
-156
lines changed

2 files changed

+143
-156
lines changed

src/MongoDB.Driver/Encryption/CsfleSchemaBuilder.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ internal EncryptedCollectionBuilder()
8080
/// </summary>
8181
public EncryptedCollectionBuilder<TDocument> EncryptMetadata(Guid? keyId = null, CsfleEncryptionAlgorithm? algorithm = null)
8282
{
83+
if (keyId is null && algorithm is null)
84+
{
85+
throw new ArgumentException("At least one of keyId or algorithm must be specified.");
86+
}
87+
8388
_schema["encryptMetadata"] = new BsonDocument
8489
{
8590
{ "keyId", () => new BsonArray { new BsonBinaryData(keyId!.Value, GuidRepresentation.Standard) }, keyId is not null },
@@ -209,7 +214,18 @@ private static BsonDocument CreateEncryptDocument(
209214
CsfleEncryptionAlgorithm? algorithm = null,
210215
Guid? keyId = null)
211216
{
217+
if (bsonTypes == null)
218+
{
219+
throw new ArgumentNullException(nameof(bsonTypes));
220+
}
221+
212222
var convertedBsonTypes = bsonTypes.Select(MapBsonTypeToString).ToList();
223+
224+
if (convertedBsonTypes.Count == 0)
225+
{
226+
throw new ArgumentException("At least one BSON type must be specified.", nameof(bsonTypes));
227+
}
228+
213229
BsonValue bsonTypeVal = convertedBsonTypes.Count == 1
214230
? convertedBsonTypes[0]
215231
: new BsonArray(convertedBsonTypes);
@@ -292,7 +308,7 @@ private static string MapCsfleEncyptionAlgorithmToString(CsfleEncryptionAlgorith
292308
}
293309

294310
/// <summary>
295-
/// The type of possible encryption algorithms. //TODO Maybe we need a more generic name?
311+
/// The type of possible encryption algorithms. //TODO Maybe we need a more generic name but EncryptionAlgorithm is already taken (it's a superset of these values)
296312
/// </summary>
297313
public enum CsfleEncryptionAlgorithm
298314
{

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

Lines changed: 126 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ public class CsfleSchemaBuilderTests
2828
private const string _keyIdString = "6f4af470-00d1-401f-ac39-f45902a0c0c8";
2929
private static Guid _keyId = Guid.Parse(_keyIdString);
3030

31-
[Fact]
32-
public void BasicPropertyTest()
31+
[Fact]
32+
public void CsfleSchemaBuilder_works_as_expected()
3333
{
3434
const string collectionName = "medicalRecords.patients";
3535

@@ -50,7 +50,18 @@ public void BasicPropertyTest()
5050
innerBuilder
5151
.Property(i => i.PolicyNumber, BsonType.Int32,
5252
CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic);
53+
})
54+
.PatternProperty("_PIIString$", BsonType.String, CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
55+
.PatternProperty("_PIIArray$", BsonType.Array, CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random)
56+
.PatternProperty(p => p.Insurance, innerBuilder =>
57+
{
58+
innerBuilder
59+
.PatternProperty("_PIIString$", BsonType.String,
60+
CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
61+
.PatternProperty("_PIINumber$", BsonType.Int32,
62+
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic);
5363
});
64+
5465
} );
5566
});
5667

@@ -93,13 +104,100 @@ public void BasicPropertyTest()
93104
}
94105
}
95106
},
107+
"patternProperties": {
108+
"_PIIString$": {
109+
"encrypt": {
110+
"bsonType": "string",
111+
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
112+
},
113+
},
114+
"_PIIArray$": {
115+
"encrypt": {
116+
"bsonType": "array",
117+
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random",
118+
},
119+
},
120+
"insurance": {
121+
"bsonType": "object",
122+
"patternProperties": {
123+
"_PIINumber$": {
124+
"encrypt": {
125+
"bsonType": "int",
126+
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
127+
},
128+
},
129+
"_PIIString$": {
130+
"encrypt": {
131+
"bsonType": "string",
132+
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
133+
},
134+
},
135+
},
136+
},
137+
},
96138
}
97139
"""
98140
};
99141

100-
AssertOutcomeBuilder(builder, expected);
142+
AssertOutcomeCsfleSchemaBuilder(builder, expected);
101143
}
102144

145+
[Fact]
146+
public void CsfleSchemaBuilder_WithMultipleTypes_works_as_expected()
147+
{
148+
const string patientCollectionName = "medicalRecords.patients";
149+
const string testClassCollectionName = "test.class";
150+
151+
var builder = CsfleSchemaBuilder.Create(schemaBuilder =>
152+
{
153+
schemaBuilder.Encrypt<Patient>(patientCollectionName, builder =>
154+
{
155+
builder
156+
.EncryptMetadata(keyId: _keyId)
157+
.Property(p => p.MedicalRecords, BsonType.Array,
158+
CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random);
159+
});
160+
161+
schemaBuilder.Encrypt<TestClass>(testClassCollectionName, builder =>
162+
{
163+
builder.Property(t => t.TestString, BsonType.String);
164+
});
165+
});
166+
167+
var expected = new Dictionary<string, string>
168+
{
169+
[patientCollectionName] = """
170+
{
171+
"bsonType": "object",
172+
"encryptMetadata": {
173+
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
174+
},
175+
"properties": {
176+
"medicalRecords": {
177+
"encrypt": {
178+
"bsonType": "array",
179+
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
180+
}
181+
},
182+
},
183+
}
184+
""",
185+
[testClassCollectionName] = """
186+
{
187+
"bsonType": "object",
188+
"properties": {
189+
"TestString": {
190+
"encrypt": {
191+
"bsonType": "string",
192+
}
193+
},
194+
}
195+
}
196+
"""
197+
};
198+
199+
AssertOutcomeCsfleSchemaBuilder(builder, expected);
200+
}
103201

104202
[Theory]
105203
[InlineData(
@@ -505,146 +603,37 @@ public void EncryptedCollection_PropertyNestedWithString_works_as_expected()
505603
AssertOutcomeCollectionBuilder(builder, expected);
506604
}
507605

508-
509606
[Fact]
510-
public void BasicPropertyTest()
607+
public void EncryptedCollection_Property_with_null_bson_types_throws()
511608
{
512-
const string collectionName = "medicalRecords.patients";
513-
514-
var builder = CsfleSchemaBuilder.Create(schemaBuilder =>
515-
{
516-
schemaBuilder.Encrypt<Patient>(collectionName, builder =>
517-
{
518-
builder
519-
.EncryptMetadata(keyId: _keyId)
520-
.Property(p => p.MedicalRecords, BsonType.Array,
521-
CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random)
522-
.Property("bloodType", BsonType.String,
523-
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random)
524-
.Property(p => p.Ssn, BsonType.Int32,
525-
CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
526-
.Property(p => p.Insurance, innerBuilder =>
527-
{
528-
innerBuilder
529-
.Property(i => i.PolicyNumber, BsonType.Int32,
530-
CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic);
531-
});
532-
} );
533-
});
534-
535-
var expected = new Dictionary<string, string>
536-
{
537-
[collectionName] = """
538-
{
539-
"bsonType": "object",
540-
"encryptMetadata": {
541-
"keyId": [{ "$binary" : { "base64" : "b0r0cADRQB+sOfRZAqDAyA==", "subType" : "04" } }]
542-
},
543-
"properties": {
544-
"insurance": {
545-
"bsonType": "object",
546-
"properties": {
547-
"policyNumber": {
548-
"encrypt": {
549-
"bsonType": "int",
550-
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
551-
}
552-
}
553-
}
554-
},
555-
"medicalRecords": {
556-
"encrypt": {
557-
"bsonType": "array",
558-
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
559-
}
560-
},
561-
"bloodType": {
562-
"encrypt": {
563-
"bsonType": "string",
564-
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
565-
}
566-
},
567-
"ssn": {
568-
"encrypt": {
569-
"bsonType": "int",
570-
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
571-
}
572-
}
573-
},
574-
}
575-
"""
576-
};
609+
var builder = new EncryptedCollectionBuilder<Patient>();
577610

578-
AssertOutcomeBuilder(builder, expected);
611+
var recordedException = Record.Exception(() => builder.Property("test", null));
612+
recordedException.Should().NotBeNull();
613+
recordedException.Should().BeOfType<ArgumentNullException>();
579614
}
580615

581616
[Fact]
582-
public void BasicPatternTest()
617+
public void EncryptedCollection_Property_with_empty_bson_types_throws()
583618
{
584-
const string collectionName = "medicalRecords.patients";
619+
var builder = new EncryptedCollectionBuilder<Patient>();
585620

586-
var builder = CsfleSchemaBuilder.Create(schemaBuilder =>
587-
{
588-
schemaBuilder.Encrypt<Patient>(collectionName, builder =>
589-
{
590-
builder
591-
.PatternProperty("_PIIString$", BsonType.String, CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
592-
.PatternProperty("_PIIArray$", BsonType.Array, CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random)
593-
.PatternProperty(p => p.Insurance, innerBuilder =>
594-
{
595-
innerBuilder
596-
.PatternProperty("_PIIString$", BsonType.String,
597-
CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic)
598-
.PatternProperty("_PIINumber$", BsonType.Int32,
599-
algorithm: CsfleEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic);
600-
});
601-
} );
602-
});
621+
var recordedException = Record.Exception(() => builder.Property("test", []));
622+
recordedException.Should().NotBeNull();
623+
recordedException.Should().BeOfType<ArgumentException>();
624+
}
603625

604-
var expected = new Dictionary<string, string>
605-
{
606-
[collectionName] = """
607-
{
608-
"bsonType": "object",
609-
"patternProperties": {
610-
"_PIIString$": {
611-
"encrypt": {
612-
"bsonType": "string",
613-
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
614-
},
615-
},
616-
"_PIIArray$": {
617-
"encrypt": {
618-
"bsonType": "array",
619-
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random",
620-
},
621-
},
622-
"insurance": {
623-
"bsonType": "object",
624-
"patternProperties": {
625-
"_PIINumber$": {
626-
"encrypt": {
627-
"bsonType": "int",
628-
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
629-
},
630-
},
631-
"_PIIString$": {
632-
"encrypt": {
633-
"bsonType": "string",
634-
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
635-
},
636-
},
637-
},
638-
},
639-
},
640-
}
641-
"""
642-
};
626+
[Fact]
627+
public void EncryptedCollection_Metadata_with_empty_algorithm_and_key_throws()
628+
{
629+
var builder = new EncryptedCollectionBuilder<Patient>();
643630

644-
AssertOutcomeBuilder(builder, expected);
631+
var recordedException = Record.Exception(() => builder.EncryptMetadata(null, null));
632+
recordedException.Should().NotBeNull();
633+
recordedException.Should().BeOfType<ArgumentException>();
645634
}
646635

647-
private void AssertOutcomeBuilder(CsfleSchemaBuilder builder, Dictionary<string, string> expectedSchema)
636+
private void AssertOutcomeCsfleSchemaBuilder(CsfleSchemaBuilder builder, Dictionary<string, string> expectedSchema)
648637
{
649638
var builtSchema = builder.Build();
650639
expectedSchema.Should().HaveCount(builtSchema.Count);
@@ -655,37 +644,19 @@ private void AssertOutcomeBuilder(CsfleSchemaBuilder builder, Dictionary<string,
655644
}
656645
}
657646

658-
//TODO Give better name...
659647
private void AssertOutcomeCollectionBuilder<T>(EncryptedCollectionBuilder<T> builder, string expected)
660648
{
661649
var builtSchema = builder.Build();
662650
var expectedSchema = BsonDocument.Parse(expected);
663651
builtSchema.Should().BeEquivalentTo(expectedSchema);
664652
}
665653

666-
/** To test:
667-
* - Metadata
668-
* - *Property with expression
669-
* - *Property with string
670-
* - *Property with single bsonType
671-
* - *Property with multiple bsonType
672-
* - *Pattern property with string
673-
* - *Pattern property with multiple bsonType
674-
* - *Nested property with expression
675-
* - *Nested property with string
676-
* - *Nested pattern property with expression
677-
* - *Nested pattern property with string
678-
*
679-
* - Multiple types in schema
680-
* - Property and pattern property together
681-
* - Do it with BsonDocument....?
682-
*
683-
* ERRORS
684-
* - No empty properties and empty pattern properties?
685-
* - No empty schema
686-
* - Wrong string
687-
* - Empty bson type array
688-
*/
654+
internal class TestClass
655+
{
656+
public ObjectId Id { get; set; }
657+
658+
public string TestString { get; set; }
659+
}
689660

690661
internal class Patient
691662
{

0 commit comments

Comments
 (0)