-
Notifications
You must be signed in to change notification settings - Fork 1.3k
CSHARP-4255: Automatically create Queryable Encryption keys. #961
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
Changes from 2 commits
1f05fe2
71e171e
9318348
2b8e02a
8b8705c
c0b7f6a
c0e4ab1
6013fc2
4e6d6b9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,6 +76,40 @@ public static BsonDocument GetEffectiveEncryptedFields(CollectionNamespace colle | |
} | ||
} | ||
|
||
public static IEnumerable<BsonDocument> IterateEmptyKeyIds(CollectionNamespace collectionNamespace, BsonDocument encryptedFields) | ||
{ | ||
if (!EncryptedCollectionHelper.TryGetEffectiveEncryptedFields(collectionNamespace, encryptedFields, encryptedFieldsMap: null, out var storedEncryptedFields)) | ||
{ | ||
throw new InvalidOperationException("There are no encrypted fields defined for the collection."); | ||
} | ||
|
||
if (storedEncryptedFields.TryGetValue("fields", out var fields) && fields is BsonArray fieldsArray) | ||
{ | ||
foreach (var field in fieldsArray) | ||
{ | ||
if (field is BsonDocument fieldDocument) | ||
{ | ||
if (fieldDocument.TryGetElement("keyId", out var keyId) && keyId.Value == BsonNull.Value) | ||
{ | ||
yield return fieldDocument; | ||
} | ||
} | ||
else | ||
{ | ||
// If `F` is not a document element, skip it. | ||
continue; | ||
} | ||
} | ||
} | ||
|
||
yield break; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not needed? |
||
} | ||
|
||
public static void ModifyEndryptedFields(BsonDocument fieldDocument, Guid dataKey) | ||
{ | ||
fieldDocument["keyId"] = new BsonBinaryData(dataKey, GuidRepresentation.Standard); | ||
} | ||
|
||
public enum HelperCollectionForEncryption | ||
{ | ||
Esc, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,6 +78,64 @@ public BsonDocument AddAlternateKeyName(Guid id, string alternateKeyName, Cancel | |
public Task<BsonDocument> AddAlternateKeyNameAsync(Guid id, string alternateKeyName, CancellationToken cancellationToken = default) => | ||
_libMongoCryptController.AddAlternateKeyNameAsync(id, alternateKeyName, cancellationToken); | ||
|
||
/// <summary> | ||
/// Create encrypted collection. | ||
/// </summary> | ||
/// <param name="collectionNamespace">The collection namespace.</param> | ||
/// <param name="createCollectionOptions">The create collection options.</param> | ||
/// <param name="kmsProvider">The kms provider.</param> | ||
/// <param name="dataKeyOptions">The datakey options.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
public (IMongoCollection<TCollection> Collection, BsonDocument EncryptedFields) CreateEncryptedCollection<TCollection>(CollectionNamespace collectionNamespace, CreateCollectionOptions createCollectionOptions, string kmsProvider, DataKeyOptions dataKeyOptions, CancellationToken cancellationToken = default) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm going to discuss whether we can add a different return type than this, so might be not the final form There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've discussed this question with spec author and he's confirmed that we can define public API in the consistent way with previous driver's API methods. So, I consider 2 changes:
I'm not too confident about these changes (as well as about the initial requirement), but I would probably want to avoid returning tuple in public API, thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think Tuples are not recommended in public APIs. Agreed that there is no need to return the Do we need to return anything? Also, shouldn't the type parameter be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
yeah, I also think it can be simply There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. changed |
||
{ | ||
var effectiveEncryptedFields = createCollectionOptions?.EncryptedFields?.DeepClone()?.AsBsonDocument; | ||
|
||
foreach (var fieldDocument in EncryptedCollectionHelper.IterateEmptyKeyIds(collectionNamespace, effectiveEncryptedFields)) | ||
{ | ||
var dataKey = CreateDataKey(kmsProvider, dataKeyOptions, cancellationToken); | ||
EncryptedCollectionHelper.ModifyEndryptedFields(fieldDocument, dataKey); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. true |
||
} | ||
|
||
var database = _libMongoCryptController.KeyVaultClient.GetDatabase(collectionNamespace.DatabaseNamespace.DatabaseName); | ||
|
||
createCollectionOptions.EncryptedFields = effectiveEncryptedFields; | ||
|
||
database.CreateCollection(collectionNamespace.CollectionName, createCollectionOptions, cancellationToken); | ||
|
||
var collection = database.GetCollection<TCollection>(collectionNamespace.CollectionName); | ||
|
||
return (collection, effectiveEncryptedFields); | ||
} | ||
|
||
/// <summary> | ||
/// Create encrypted collection. | ||
/// </summary> | ||
/// <param name="collectionNamespace">The collection namespace.</param> | ||
/// <param name="createCollectionOptions">The create collection options.</param> | ||
/// <param name="kmsProvider">The kms provider.</param> | ||
/// <param name="dataKeyOptions">The datakey options.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
public async Task<(IMongoCollection<TCollection> Collection, BsonDocument EncryptedFields)> CreateEncryptedCollectionAsync<TCollection>(CollectionNamespace collectionNamespace, CreateCollectionOptions createCollectionOptions, string kmsProvider, DataKeyOptions dataKeyOptions, CancellationToken cancellationToken = default) | ||
{ | ||
var effectiveEncryptedFields = createCollectionOptions?.EncryptedFields?.DeepClone()?.AsBsonDocument; | ||
|
||
foreach (var fieldDocument in EncryptedCollectionHelper.IterateEmptyKeyIds(collectionNamespace, effectiveEncryptedFields)) | ||
{ | ||
var dataKey = await CreateDataKeyAsync(kmsProvider, dataKeyOptions, cancellationToken).ConfigureAwait(false); | ||
EncryptedCollectionHelper.ModifyEndryptedFields(fieldDocument, dataKey); | ||
} | ||
|
||
var database = _libMongoCryptController.KeyVaultClient.GetDatabase(collectionNamespace.DatabaseNamespace.DatabaseName); | ||
|
||
createCollectionOptions.EncryptedFields = effectiveEncryptedFields; | ||
|
||
await database.CreateCollectionAsync(collectionNamespace.CollectionName, createCollectionOptions, cancellationToken).ConfigureAwait(false); | ||
|
||
var collection = database.GetCollection<TCollection>(collectionNamespace.CollectionName); | ||
|
||
return (collection, effectiveEncryptedFields); | ||
} | ||
|
||
/// <summary> | ||
/// An alias function equivalent to createKey. | ||
/// </summary> | ||
|
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.
fieldsArray.OfType<BsonDocument>()
?or even
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.
good point, I like first one, second one probably is a bit harder to read, done