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
CSHARP-4255: Fix bug and some tests. #993
Changes from 4 commits
ca108c1
28cf23d
4029fd3
308349b
e7b9fc1
1e78cdb
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 |
---|---|---|
|
@@ -82,59 +82,85 @@ public ClientEncryption(ClientEncryptionOptions clientEncryptionOptions) | |
/// <summary> | ||
/// Create encrypted collection. | ||
/// </summary> | ||
/// <param name="collectionNamespace">The collection namespace.</param> | ||
/// <param name="database">The database.</param> | ||
/// <param name="collectionName">The collection name.</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> | ||
/// <returns>The operation result.</returns> | ||
/// <remarks> | ||
/// if EncryptionFields contains a keyId with a null value, a data key will be automatically generated and assigned to keyId value. | ||
/// </remarks> | ||
public void CreateEncryptedCollection<TCollection>(CollectionNamespace collectionNamespace, CreateCollectionOptions createCollectionOptions, string kmsProvider, DataKeyOptions dataKeyOptions, CancellationToken cancellationToken = default) | ||
public CreateEncryptedCollectionResult CreateEncryptedCollection(IMongoDatabase database, string collectionName, CreateCollectionOptions createCollectionOptions, string kmsProvider, DataKeyOptions dataKeyOptions, CancellationToken cancellationToken = default) | ||
{ | ||
Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); | ||
Ensure.IsNotNull(database, nameof(database)); | ||
Ensure.IsNotNull(collectionName, nameof(collectionName)); | ||
Ensure.IsNotNull(createCollectionOptions, nameof(createCollectionOptions)); | ||
Ensure.IsNotNull(dataKeyOptions, nameof(dataKeyOptions)); | ||
Ensure.IsNotNull(kmsProvider, nameof(kmsProvider)); | ||
|
||
foreach (var fieldDocument in EncryptedCollectionHelper.IterateEmptyKeyIds(collectionNamespace, createCollectionOptions.EncryptedFields)) | ||
var encryptedFields = createCollectionOptions.EncryptedFields?.DeepClone()?.AsBsonDocument; | ||
try | ||
{ | ||
var dataKey = CreateDataKey(kmsProvider, dataKeyOptions, cancellationToken); | ||
EncryptedCollectionHelper.ModifyEncryptedFields(fieldDocument, dataKey); | ||
foreach (var fieldDocument in EncryptedCollectionHelper.IterateEmptyKeyIds(new CollectionNamespace(database.DatabaseNamespace.DatabaseName, collectionName), encryptedFields)) | ||
{ | ||
var dataKey = CreateDataKey(kmsProvider, dataKeyOptions, cancellationToken); | ||
EncryptedCollectionHelper.ModifyEncryptedFields(fieldDocument, dataKey); | ||
} | ||
|
||
var effectiveCreateEncryptionOptions = createCollectionOptions.Clone(); | ||
effectiveCreateEncryptionOptions.EncryptedFields = encryptedFields; | ||
database.CreateCollection(collectionName, effectiveCreateEncryptionOptions, cancellationToken); | ||
} | ||
catch (Exception ex) | ||
{ | ||
throw new MongoEncryptionCreateCollectionException(ex, encryptedFields); | ||
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. Returning operation data, which is unrelated to exception, within exception is a bit unusual, but I am fine with this here. 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. Discussed with Dima via Slack. While from an API perspective it feels odd to return successfully-created keys in the exception, I don't see another reasonable way to avoid phantom encryption keys. While we could modify |
||
} | ||
|
||
var database = _libMongoCryptController.KeyVaultClient.GetDatabase(collectionNamespace.DatabaseNamespace.DatabaseName); | ||
|
||
database.CreateCollection(collectionNamespace.CollectionName, createCollectionOptions, cancellationToken); | ||
return new CreateEncryptedCollectionResult(encryptedFields); | ||
} | ||
|
||
/// <summary> | ||
/// Create encrypted collection. | ||
/// </summary> | ||
/// <param name="collectionNamespace">The collection namespace.</param> | ||
/// <param name="database">The database.</param> | ||
/// <param name="collectionName">The collection name.</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> | ||
/// <returns>The operation result.</returns> | ||
/// <remarks> | ||
/// if EncryptionFields contains a keyId with a null value, a data key will be automatically generated and assigned to keyId value. | ||
/// </remarks> | ||
public async Task CreateEncryptedCollectionAsync<TCollection>(CollectionNamespace collectionNamespace, CreateCollectionOptions createCollectionOptions, string kmsProvider, DataKeyOptions dataKeyOptions, CancellationToken cancellationToken = default) | ||
public async Task<CreateEncryptedCollectionResult> CreateEncryptedCollectionAsync(IMongoDatabase database, string collectionName, CreateCollectionOptions createCollectionOptions, string kmsProvider, DataKeyOptions dataKeyOptions, CancellationToken cancellationToken = default) | ||
{ | ||
Ensure.IsNotNull(collectionNamespace, nameof(collectionNamespace)); | ||
Ensure.IsNotNull(database, nameof(database)); | ||
Ensure.IsNotNull(collectionName, nameof(collectionName)); | ||
Ensure.IsNotNull(createCollectionOptions, nameof(createCollectionOptions)); | ||
Ensure.IsNotNull(dataKeyOptions, nameof(dataKeyOptions)); | ||
Ensure.IsNotNull(kmsProvider, nameof(kmsProvider)); | ||
|
||
foreach (var fieldDocument in EncryptedCollectionHelper.IterateEmptyKeyIds(collectionNamespace, createCollectionOptions.EncryptedFields)) | ||
var encryptedFields = createCollectionOptions.EncryptedFields?.DeepClone()?.AsBsonDocument; | ||
try | ||
{ | ||
var dataKey = await CreateDataKeyAsync(kmsProvider, dataKeyOptions, cancellationToken).ConfigureAwait(false); | ||
EncryptedCollectionHelper.ModifyEncryptedFields(fieldDocument, dataKey); | ||
foreach (var fieldDocument in EncryptedCollectionHelper.IterateEmptyKeyIds(new CollectionNamespace(database.DatabaseNamespace.DatabaseName, collectionName), encryptedFields)) | ||
{ | ||
var dataKey = await CreateDataKeyAsync(kmsProvider, dataKeyOptions, cancellationToken).ConfigureAwait(false); | ||
EncryptedCollectionHelper.ModifyEncryptedFields(fieldDocument, dataKey); | ||
} | ||
|
||
var effectiveCreateEncryptionOptions = createCollectionOptions.Clone(); | ||
effectiveCreateEncryptionOptions.EncryptedFields = encryptedFields; | ||
await database.CreateCollectionAsync(collectionName, effectiveCreateEncryptionOptions, cancellationToken).ConfigureAwait(false); | ||
} | ||
catch (Exception ex) | ||
{ | ||
throw new MongoEncryptionCreateCollectionException(ex, encryptedFields); | ||
} | ||
|
||
var database = _libMongoCryptController.KeyVaultClient.GetDatabase(collectionNamespace.DatabaseNamespace.DatabaseName); | ||
|
||
await database.CreateCollectionAsync(collectionNamespace.CollectionName, createCollectionOptions, cancellationToken).ConfigureAwait(false); | ||
return new CreateEncryptedCollectionResult(encryptedFields); | ||
} | ||
|
||
/// <summary> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* 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 MongoDB.Bson; | ||
|
||
namespace MongoDB.Driver.Encryption | ||
{ | ||
/// <summary> | ||
/// Represents the result of a create encrypted collection. | ||
/// </summary> | ||
BorisDog marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public sealed class CreateEncryptedCollectionResult | ||
{ | ||
private readonly BsonDocument _encryptedFields; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="CreateEncryptedCollectionResult"/> class. | ||
/// </summary> | ||
/// <param name="encryptedFields">The encrypted fields document.</param> | ||
public CreateEncryptedCollectionResult(BsonDocument encryptedFields) => _encryptedFields = encryptedFields; | ||
|
||
/// <summary> | ||
/// The encrypted fields document. | ||
/// </summary> | ||
public BsonDocument EncryptedFields => _encryptedFields; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/* 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.Runtime.Serialization; | ||
using MongoDB.Bson; | ||
|
||
namespace MongoDB.Driver.Encryption | ||
{ | ||
/// <summary> | ||
/// Represents an encryption exception. | ||
/// </summary> | ||
[Serializable] | ||
public class MongoEncryptionCreateCollectionException : MongoEncryptionException | ||
BorisDog marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
private readonly BsonDocument _encryptedFields; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="MongoEncryptionException"/> class. | ||
/// </summary> | ||
/// <param name="innerException">The inner exception.</param> | ||
/// <param name="encryptedFields">The encrypted fields.</param> | ||
public MongoEncryptionCreateCollectionException(Exception innerException, BsonDocument encryptedFields) | ||
: base(innerException) | ||
{ | ||
_encryptedFields = encryptedFields; | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="MongoEncryptionCreateCollectionException"/> class (this overload used by deserialization). | ||
/// </summary> | ||
/// <param name="info">The SerializationInfo.</param> | ||
/// <param name="context">The StreamingContext.</param> | ||
protected MongoEncryptionCreateCollectionException(SerializationInfo info, StreamingContext context) | ||
: base(info, context) | ||
{ | ||
_encryptedFields = (BsonDocument)info.GetValue(nameof(_encryptedFields), typeof(BsonDocument)); | ||
} | ||
|
||
/// <summary> | ||
/// The encrypted fields. | ||
/// </summary> | ||
public BsonDocument EncryptedFields => _encryptedFields; | ||
|
||
// public methods | ||
/// <summary> | ||
/// Gets the object data. | ||
/// </summary> | ||
/// <param name="info">The information.</param> | ||
/// <param name="context">The context.</param> | ||
public override void GetObjectData(SerializationInfo info, StreamingContext context) | ||
{ | ||
base.GetObjectData(info, context); | ||
info.AddValue(nameof(_encryptedFields), _encryptedFields); | ||
} | ||
} | ||
} |
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.
nit: you could use initialization syntax.
var = new CreateCollectionOptions() { x= y ... }
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