diff --git a/test/Core.Test/Core.Test.csproj b/test/Core.Test/Core.Test.csproj
index baace97710..cc19c50c35 100644
--- a/test/Core.Test/Core.Test.csproj
+++ b/test/Core.Test/Core.Test.csproj
@@ -10,6 +10,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
+
diff --git a/test/Core.Test/Platform/X509ChainCustomization/X509ChainCustomizationServiceCollectionExtensionsTests.cs b/test/Core.Test/Platform/X509ChainCustomization/X509ChainCustomizationServiceCollectionExtensionsTests.cs
index d4f927f4a1..703a37cc30 100644
--- a/test/Core.Test/Platform/X509ChainCustomization/X509ChainCustomizationServiceCollectionExtensionsTests.cs
+++ b/test/Core.Test/Platform/X509ChainCustomization/X509ChainCustomizationServiceCollectionExtensionsTests.cs
@@ -5,6 +5,7 @@ using Bit.Core.Settings;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NSubstitute;
using Xunit;
@@ -30,10 +31,6 @@ public class X509ChainCustomizationServiceCollectionExtensionsTests
var services = CreateServices((gs, environment, config) =>
{
- gs.SelfHosted = true;
-
- environment.EnvironmentName = "Development";
-
config["X509ChainOptions:AdditionalCustomTrustCertificatesDirectory"] = tempDir.FullName;
});
@@ -69,8 +66,6 @@ public class X509ChainCustomizationServiceCollectionExtensionsTests
{
gs.SelfHosted = false;
- environment.EnvironmentName = "Development";
-
config["X509ChainOptions:AdditionalCustomTrustCertificatesDirectory"] = tempDir.FullName;
});
@@ -85,12 +80,8 @@ public class X509ChainCustomizationServiceCollectionExtensionsTests
var tempCert = Path.Combine(tempDir.FullName, "test.crt");
await File.WriteAllBytesAsync(tempCert, CreateSelfSignedCert("localhost").Export(X509ContentType.Cert));
- var options = CreateOptions((gs, environment, config) =>
+ var services = CreateServices((gs, environment, config) =>
{
- gs.SelfHosted = false;
-
- environment.EnvironmentName = "Development";
-
config["X509ChainOptions:AdditionalCustomTrustCertificatesDirectory"] = tempDir.FullName;
}, services =>
{
@@ -100,9 +91,95 @@ public class X509ChainCustomizationServiceCollectionExtensionsTests
});
});
+ var options = services.GetRequiredService>().Value;
+
Assert.True(options.TryGetCustomRemoteCertificateValidationCallback(out var callback));
var cert = Assert.Single(options.AdditionalCustomTrustCertificates);
Assert.Equal("CN=example.com", cert.Subject);
+
+ var fakeLogCollector = services.GetFakeLogCollector();
+
+ Assert.Contains(fakeLogCollector.GetSnapshot(),
+ r => r.Message == $"Additional custom trust certificates were added directly, skipping loading them from '{tempDir}'");
+ }
+
+ [Fact]
+ public void NullCustomDirectory_SkipsTryingToLoad()
+ {
+ var services = CreateServices((gs, environment, config) =>
+ {
+ config["X509ChainOptions:AdditionalCustomTrustCertificatesDirectory"] = null;
+ });
+
+ var options = services.GetRequiredService>().Value;
+
+ Assert.False(options.TryGetCustomRemoteCertificateValidationCallback(out _));
+ }
+
+ [Theory]
+ [InlineData("Development", LogLevel.Debug)]
+ [InlineData("Production", LogLevel.Warning)]
+ public void CustomDirectoryDoesNotExist_Logs(string environment, LogLevel logLevel)
+ {
+ var fakeDir = "/fake/dir/that/does/not/exist";
+ var services = CreateServices((gs, hostEnvironment, config) =>
+ {
+ hostEnvironment.EnvironmentName = environment;
+
+ config["X509ChainOptions:AdditionalCustomTrustCertificatesDirectory"] = fakeDir;
+ });
+
+ var options = services.GetRequiredService>().Value;
+
+ Assert.False(options.TryGetCustomRemoteCertificateValidationCallback(out _));
+
+ var fakeLogCollector = services.GetFakeLogCollector();
+
+ Assert.Contains(fakeLogCollector.GetSnapshot(),
+ r => r.Message == $"An additional custom trust certificate directory was given '{fakeDir}' but that directory does not exist."
+ && r.Level == logLevel
+ );
+ }
+
+ [Fact]
+ public async Task NamedOptions_NotConfiguredAsync()
+ {
+ // To help make sure this fails for the right reason we should add certs to the directory
+ var tempDir = Directory.CreateTempSubdirectory("certs");
+
+ var tempCert = Path.Combine(tempDir.FullName, "test.crt");
+ await File.WriteAllBytesAsync(tempCert, CreateSelfSignedCert("localhost").Export(X509ContentType.Cert));
+
+ var services = CreateServices((gs, environment, config) =>
+ {
+ config["X509ChainOptions:AdditionalCustomTrustCertificatesDirectory"] = tempDir.FullName;
+ });
+
+ var options = services.GetRequiredService>();
+
+ var namedOptions = options.Get("SomeName");
+
+ Assert.Null(namedOptions.AdditionalCustomTrustCertificates);
+ }
+
+ [Fact]
+ public void CustomLocation_NoCertificates_Logs()
+ {
+ var tempDir = Directory.CreateTempSubdirectory("certs");
+ var services = CreateServices((gs, hostEnvironment, config) =>
+ {
+ config["X509ChainOptions:AdditionalCustomTrustCertificatesDirectory"] = tempDir.FullName;
+ });
+
+ var options = services.GetRequiredService>().Value;
+
+ Assert.False(options.TryGetCustomRemoteCertificateValidationCallback(out _));
+
+ var fakeLogCollector = services.GetFakeLogCollector();
+
+ Assert.Contains(fakeLogCollector.GetSnapshot(),
+ r => r.Message == $"No additional custom trust certificates were found in '{tempDir.FullName}'"
+ );
}
private static X509ChainOptions CreateOptions(Action> configure, Action? after = null)
@@ -113,13 +190,23 @@ public class X509ChainCustomizationServiceCollectionExtensionsTests
private static IServiceProvider CreateServices(Action> configure, Action? after = null)
{
- var globalSettings = new GlobalSettings();
+ var globalSettings = new GlobalSettings
+ {
+ // A solid default for these tests as these settings aren't allowed to work in cloud.
+ SelfHosted = true,
+ };
var hostEnvironment = Substitute.For();
+ hostEnvironment.EnvironmentName = "Development";
var config = new Dictionary();
configure(globalSettings, hostEnvironment, config);
var services = new ServiceCollection();
+ services.AddLogging(logging =>
+ {
+ logging.SetMinimumLevel(LogLevel.Debug);
+ logging.AddFakeLogging();
+ });
services.AddSingleton(globalSettings);
services.AddSingleton(hostEnvironment);
services.AddSingleton(