|
16 | 16 | using System;
|
17 | 17 | using System.Collections;
|
18 | 18 | using System.Collections.Generic;
|
19 |
| -using System.Collections.ObjectModel; |
| 19 | +using System.Diagnostics; |
| 20 | +using System.IO; |
20 | 21 | using System.Linq;
|
21 | 22 | using System.Security.Cryptography.X509Certificates;
|
22 | 23 | using FluentAssertions;
|
23 | 24 | using MongoDB.Bson;
|
| 25 | +using MongoDB.Driver.Core.Clusters; |
24 | 26 | using MongoDB.Driver.Core.Misc;
|
| 27 | +using MongoDB.Driver.Encryption; |
25 | 28 |
|
26 | 29 | namespace MongoDB.Driver.Tests.Specifications.client_side_encryption
|
27 | 30 | {
|
28 | 31 | public static class EncryptionTestHelper
|
29 | 32 | {
|
30 | 33 | private static readonly IReadOnlyDictionary<string, IReadOnlyDictionary<string, object>> __kmsProviders;
|
| 34 | + private static readonly string __defaultMongocryptdPath = Environment.GetEnvironmentVariable("MONGODB_BINARIES") ?? ""; |
| 35 | + private static readonly Lazy<(bool IsValid, SemanticVersion Version)> __defaultCsfleSetupState = new Lazy<(bool IsValid, SemanticVersion Version)>(IsDefaultCsfleSetupValid, isThreadSafe: true); |
31 | 36 |
|
32 | 37 | static EncryptionTestHelper()
|
33 | 38 | {
|
@@ -102,10 +107,14 @@ public static void ConfigureDefaultExtraOptions(Dictionary<string, object> extra
|
102 | 107 | {
|
103 | 108 | Ensure.IsNotNull(extraOptions, nameof(extraOptions));
|
104 | 109 |
|
105 |
| - if (!extraOptions.ContainsKey("mongocryptdSpawnPath")) |
| 110 | + if (!extraOptions.TryGetValue("mongocryptdSpawnPath", out object value)) |
106 | 111 | {
|
107 |
| - var mongocryptdSpawnPath = Environment.GetEnvironmentVariable("MONGODB_BINARIES") ?? ""; |
108 |
| - extraOptions.Add("mongocryptdSpawnPath", mongocryptdSpawnPath); |
| 112 | + extraOptions.Add("mongocryptdSpawnPath", __defaultMongocryptdPath); |
| 113 | + |
| 114 | + if (!__defaultCsfleSetupState.Value.IsValid) |
| 115 | + { |
| 116 | + throw new Exception($"The configured mongocryptd version {__defaultCsfleSetupState.Value.Version} doesn't match the server version {CoreTestConfiguration.ServerVersion}."); |
| 117 | + } |
109 | 118 | }
|
110 | 119 |
|
111 | 120 | var mongocryptdPort = Environment.GetEnvironmentVariable("FLE_MONGOCRYPTD_PORT");
|
@@ -268,5 +277,72 @@ public static IReadOnlyDictionary<string, IReadOnlyDictionary<string, object>> P
|
268 | 277 | bool IsPlaceholder(BsonValue value) => value.IsBsonDocument && value.AsBsonDocument.Contains("$$placeholder");
|
269 | 278 | string GetFromEnvVariables(string kmsProvider, string key) => kmsProvidersFromEnvs[kmsProvider][key].ToString();
|
270 | 279 | }
|
| 280 | + |
| 281 | + // private methods |
| 282 | + /// <summary> |
| 283 | + /// Ensure that used mongocryptd corresponds to used server. |
| 284 | + /// </summary> |
| 285 | + private static (bool IsValid, SemanticVersion MongocryptdVersion) IsDefaultCsfleSetupValid() |
| 286 | + { |
| 287 | + var cryptSharedLibPath = CoreTestConfiguration.GetCryptSharedLibPath(); |
| 288 | + if (cryptSharedLibPath != null) |
| 289 | + { |
| 290 | + var dummyNamespace = CollectionNamespace.FromFullName("db.dummy"); |
| 291 | + var autoEncryptionOptions = new AutoEncryptionOptions( |
| 292 | + dummyNamespace, |
| 293 | + kmsProviders: GetKmsProviders(filter: "local"), |
| 294 | + extraOptions: new Dictionary<string, object>() { { "cryptSharedLibPath", cryptSharedLibPath } }); |
| 295 | + using (var cryptClient = CryptClientCreator.CreateCryptClient(autoEncryptionOptions.ToCryptClientSettings())) |
| 296 | + { |
| 297 | + if (cryptClient.CryptSharedLibraryVersion != null) |
| 298 | + { |
| 299 | + // csfle shared library code path |
| 300 | + return (IsValid: true, MongocryptdVersion: null); |
| 301 | + } |
| 302 | + else |
| 303 | + { |
| 304 | + // we will still try using mongocryptd |
| 305 | + } |
| 306 | + } |
| 307 | + } |
| 308 | + |
| 309 | + var configuredMongocryptdVersion = GetMongocryptdVersion(__defaultMongocryptdPath); |
| 310 | + if (CompareSemanticVersionsAsReleased(configuredMongocryptdVersion, CoreTestConfiguration.ServerVersion) < 0) |
| 311 | + { |
| 312 | + return (IsValid: false, MongocryptdVersion: configuredMongocryptdVersion); |
| 313 | + } |
| 314 | + |
| 315 | + return (IsValid: true, MongocryptdVersion: configuredMongocryptdVersion); |
| 316 | + |
| 317 | + SemanticVersion GetMongocryptdVersion(string mongocryptdPath) |
| 318 | + { |
| 319 | + mongocryptdPath = File.Exists(mongocryptdPath) ? mongocryptdPath : Path.Combine(mongocryptdPath, "mongocryptd"); |
| 320 | + using (var process = new Process()) |
| 321 | + { |
| 322 | + process.StartInfo.RedirectStandardOutput = true; |
| 323 | + process.StartInfo.Arguments = "--version"; |
| 324 | + process.StartInfo.FileName = mongocryptdPath; |
| 325 | + process.StartInfo.CreateNoWindow = true; |
| 326 | + process.StartInfo.UseShellExecute = false; |
| 327 | + |
| 328 | + if (!process.Start()) |
| 329 | + { |
| 330 | + // skip it. This case can happen if no new process resource is started |
| 331 | + // (for example, if an existing process is reused) |
| 332 | + } |
| 333 | + |
| 334 | + var output = process.StandardOutput.ReadToEnd(); |
| 335 | + var buildInfoBody = output.Substring(output.IndexOf("Build Info") + 12 /*key length*/); |
| 336 | + return SemanticVersion.Parse(buildInfoBody); |
| 337 | + } |
| 338 | + } |
| 339 | + |
| 340 | + int CompareSemanticVersionsAsReleased(SemanticVersion a, SemanticVersion b) |
| 341 | + { |
| 342 | + var aReleased = new SemanticVersion(a.Major, a.Minor, a.Patch); |
| 343 | + var bReleased = new SemanticVersion(b.Major, b.Minor, b.Patch); |
| 344 | + return aReleased.CompareTo(bReleased); |
| 345 | + } |
| 346 | + } |
271 | 347 | }
|
272 | 348 | }
|
0 commit comments