-
Notifications
You must be signed in to change notification settings - Fork 1.3k
CSHARP-4149: Add FLE 2 behavior for CreateCollection() and Collection.Drop(). #790
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@@ -19,7 +19,9 @@ | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using MongoDB.Bson; | |||
using MongoDB.Bson.Serialization.Serializers; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no real changes here, I just removed original CreateIndexesOperation
and renamed CreateIndexesUsingCommandOperation
to CreateIndexesOperation
. This change was missed in one of the previous tickets and it a bit affected tests I wrote in this ticket
}); | ||
} | ||
|
||
private async Task<IWriteOperation<BsonDocument>> CreateDropCollectionOperationAsync(string name, DropCollectionOptions options, IClientSessionHandle session, CancellationToken cancellationToken) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's unfortunate that creating collection step requires calling a server, so now we need to have 2 versions of this method: sync and async
@@ -824,7 +908,7 @@ private MessageEncoderSettings GetMessageEncoderSettings() | |||
#pragma warning disable 618 | |||
if (BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V2) | |||
{ | |||
messageEncoderSettings.Add(MessageEncoderSettingsName.GuidRepresentation, _settings.GuidRepresentation); | |||
messageEncoderSettings.Add(MessageEncoderSettingsName.GuidRepresentation, withEncryption ? GuidRepresentation.Unspecified : _settings.GuidRepresentation); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rstam can you please take a look at this change? In 2 words, since create/drop collection with FLE happens without libmongocrypt (ie LibMongoCryptController) call, but server still expects subType=4 in the provided FLE configuration (encryptedFields), we have to configure correct guidRepresentation manually. In this case we rely on what was provided in the schema
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need a withEncryption
parameter at all, you can just do:
messageEncoderSettings.Add(MessageEncoderSettingsName.GuidRepresentation, GuidRepresentation.Unspecified);
whether encryption is configured or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And it is correct to only do this when GuidRepresentationMode
is V2
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
discussed offline
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly LGTM, some minor comments.
src/MongoDB.Driver.Core/Core/Encryption/EncryptedCollectionHelper.cs
Outdated
Show resolved
Hide resolved
src/MongoDB.Driver.Core/Core/Encryption/EncryptedCollectionHelper.cs
Outdated
Show resolved
Hide resolved
src/MongoDB.Driver.Core/Core/Operations/CreateCollectionOperation.cs
Outdated
Show resolved
Hide resolved
src/MongoDB.Driver.Core/Core/Operations/CompositeWriteOperation.cs
Outdated
Show resolved
Hide resolved
...MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs
Outdated
Show resolved
Hide resolved
...MongoDB.Driver.Tests/Specifications/client-side-encryption/ClientSideEncryptionTestRunner.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, two minor comments for you considerations.
{ | ||
if (autoEncryptionOptions != null) | ||
{ | ||
var listCollectionOptions = new ListCollectionsOptions() { Filter = $"{{ name : '{collectionNamespace.CollectionName}' }}" }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor: I'd combine the two conditions into single statement.
var subject = new CompositeWriteOperation<BsonDocument>((healthyOperation1.Object, IsMainOperation: false), (faultyOperation2.Object, IsMainOperation: false), (healthyOperation3.Object, IsMainOperation: true)); | ||
|
||
var resultedException = async | ||
? Record.Exception(() => subject.ExecuteAsync(Mock.Of<IWriteBinding>(), CancellationToken.None).GetAwaiter().GetResult()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure that it's crucial to change all the old tests. But lots of new new tests are definitely using the usual TPL oriented approach.
#pragma warning restore | ||
var collectionNamespace = new CollectionNamespace(_databaseNamespace, name); | ||
|
||
_ = EncryptedCollectionHelper.TryGetEffectiveEncryptedFields(collectionNamespace, options.EncryptedFields, _client.Settings?.AutoEncryptionOptions?.EncryptedFieldsMap, out var effectiveEncryptedFields); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can you ignore the return value? What if TryGetEffectiveEncryptedFields
returns false
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This "looks" like a bug. Consider a new helper method instead:
var effectiveEncryptedFields = EncryptedCollectionHelper.GetEffectiveEncryptedFields(collectionNamespace, options.EncryptedFields, _client.Settings?.AutoEncryptionOptions?.EncryptedFieldsMap);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new helper method could be:
public static BsonDocument GetEffectiveEncryptedFields(CollectionNamespace collectionNamespace, BsonDocument encryptedFields, IReadOnlyDictionary<string, BsonDocument> encryptedFieldsMap)
{
if (TryGetEffectiveEncryptedFields(collectionNamespace, encryptedFields, encryptedFieldsMap, out var effectiveEncryptedFields))
{
return effectiveEncryptedFields;
}
else
{
return null;
}
}
I realize there's a bit of redundancy in this proposed helper method but it's very clear and doesn't rely on any side effects of the TryGetEffectiveEncryptedFields
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
var subject = new CompositeWriteOperation<BsonDocument>((healthyOperation1.Object, IsMainOperation: false), (faultyOperation2.Object, IsMainOperation: false), (healthyOperation3.Object, IsMainOperation: true)); | ||
|
||
var resultedException = async | ||
? Record.Exception(() => subject.ExecuteAsync(Mock.Of<IWriteBinding>(), CancellationToken.None).GetAwaiter().GetResult()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the only reason OLD tests don't return Task is that when they were written xUnit did not yet support that.
And there was probably some period of time between when xUnit started supporting async tests and when we became aware of it and started using that.
I think it's fine to use async tests going forward. No need to change old tests. Perhaps if we work on on old tests for some reason we can change them little by little.
@@ -291,28 +292,48 @@ public override Task CreateCollectionAsync(IClientSessionHandle session, string | |||
|
|||
public override void DropCollection(string name, CancellationToken cancellationToken) | |||
{ | |||
UsingImplicitSession(session => DropCollection(session, name, cancellationToken), cancellationToken); | |||
UsingImplicitSession(session => DropCollection(session, name, options: null, cancellationToken), cancellationToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would avoid duplicating even small amounts of logic. Simply provide the default value for the missing parameter and delegate to the next method:
DropCollection(name, options: null, cancellationToken);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
ExecuteWriteOperation(session, operation, cancellationToken); | ||
} | ||
|
||
public override Task DropCollectionAsync(string name, CancellationToken cancellationToken) | ||
{ | ||
return UsingImplicitSessionAsync(session => DropCollectionAsync(session, name, cancellationToken), cancellationToken); | ||
return UsingImplicitSessionAsync(session => DropCollectionAsync(session, name, options: null, cancellationToken), cancellationToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, avoid duplicating even the smallest amount of logic:
return DropCollectionAsync(name, options: null, cancellationToken);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -824,7 +908,7 @@ private MessageEncoderSettings GetMessageEncoderSettings() | |||
#pragma warning disable 618 | |||
if (BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V2) | |||
{ | |||
messageEncoderSettings.Add(MessageEncoderSettingsName.GuidRepresentation, _settings.GuidRepresentation); | |||
messageEncoderSettings.Add(MessageEncoderSettingsName.GuidRepresentation, withEncryption ? GuidRepresentation.Unspecified : _settings.GuidRepresentation); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need a withEncryption
parameter at all, you can just do:
messageEncoderSettings.Add(MessageEncoderSettingsName.GuidRepresentation, GuidRepresentation.Unspecified);
whether encryption is configured or not.
} | ||
|
||
return true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found the logic of the above method somewhat confusing and it took me a long time to convince myself that it could be correct. I think the following refactoring expresses the intent more clearly:
public static bool TryGetEffectiveEncryptedFields(CollectionNamespace collectionNamespace, BsonDocument encryptedFields, IReadOnlyDictionary<string, BsonDocument> encryptedFieldsMap, out BsonDocument effectiveEncryptedFields)
{
if (encryptedFields != null)
{
effectiveEncryptedFields = encryptedFields;
return true;
}
if (encryptedFieldsMap != null)
{
return encryptedFieldsMap.TryGetValue(collectionNamespace.ToString(), out effectiveEncryptedFields);
}
effectiveEncryptedFields = null;
return false;
}
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
(CreateInnerCollectionOperation(EncryptedCollectionHelper.GetAdditionalCollectionName(encryptedFields, collectionNamespace, HelperCollectionForEncryption.Esc)), IsMainOperation: false), | ||
(CreateInnerCollectionOperation(EncryptedCollectionHelper.GetAdditionalCollectionName(encryptedFields, collectionNamespace, HelperCollectionForEncryption.Ecc)), IsMainOperation: false), | ||
(CreateInnerCollectionOperation(EncryptedCollectionHelper.GetAdditionalCollectionName(encryptedFields, collectionNamespace, HelperCollectionForEncryption.Ecos)), IsMainOperation: false), | ||
(mainOperation, true), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion:
(mainOperation, IsMainOperation: true),
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -824,7 +908,7 @@ private MessageEncoderSettings GetMessageEncoderSettings() | |||
#pragma warning disable 618 | |||
if (BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V2) | |||
{ | |||
messageEncoderSettings.Add(MessageEncoderSettingsName.GuidRepresentation, _settings.GuidRepresentation); | |||
messageEncoderSettings.Add(MessageEncoderSettingsName.GuidRepresentation, withEncryption ? GuidRepresentation.Unspecified : _settings.GuidRepresentation); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And it is correct to only do this when GuidRepresentationMode
is V2
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
No description provided.