diff --git a/SecurityService.IntegrationTesting.Helpers/IntegrationTestCertificate.cs b/SecurityService.IntegrationTesting.Helpers/IntegrationTestCertificate.cs new file mode 100644 index 00000000..27e9781d --- /dev/null +++ b/SecurityService.IntegrationTesting.Helpers/IntegrationTestCertificate.cs @@ -0,0 +1,128 @@ +using System.Net; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace SecurityService.IntegrationTesting.Helpers; + +public sealed class IntegrationTestCertificate : IDisposable +{ + private const string PasswordValue = "password"; + private const string ServerCertificateFileName = "aspnetapp-web-api.pfx"; + private const string RootCertificateFileName = "aspnetapp-root-cert.cer"; + private const string ContainerCertificateDirectoryPath = "/app/test-certs"; + private const string RootSubjectName = "CN=SecurityServiceIntegrationTestRoot"; + private const string LocalhostName = "localhost"; + private const string LoopbackAddress = "127.0.0.1"; + + private readonly string _certificateDirectory; + private readonly X509Certificate2 _certificate; + private bool _trustedRootInstalled; + private bool _disposed; + + private IntegrationTestCertificate(string certificateDirectory, X509Certificate2 certificate) + { + _certificateDirectory = certificateDirectory; + _certificate = certificate; + } + + public string CertificateDirectory => _certificateDirectory; + + public string ContainerCertificateDirectory => ContainerCertificateDirectoryPath; + + public string Password => PasswordValue; + + public string RootCertificatePath => Path.Combine(_certificateDirectory, RootCertificateFileName); + + public string ServerCertificatePath => Path.Combine(_certificateDirectory, ServerCertificateFileName); + + public string Thumbprint => _certificate.Thumbprint ?? string.Empty; + + public static IntegrationTestCertificate Create() + { + string certificateDirectory = Path.Combine(Path.GetTempPath(), $"securityservice-cert-{Guid.NewGuid():N}"); + Directory.CreateDirectory(certificateDirectory); + + using RSA rsa = RSA.Create(2048); + CertificateRequest request = new CertificateRequest(RootSubjectName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + request.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, true)); + request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment, true)); + + OidCollection enhancedKeyUsages = new OidCollection(); + enhancedKeyUsages.Add(new Oid("1.3.6.1.5.5.7.3.1")); + request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(enhancedKeyUsages, false)); + + SubjectAlternativeNameBuilder subjectAlternativeNames = new SubjectAlternativeNameBuilder(); + subjectAlternativeNames.AddDnsName(LocalhostName); + subjectAlternativeNames.AddIpAddress(IPAddress.Parse(LoopbackAddress)); + request.CertificateExtensions.Add(subjectAlternativeNames.Build()); + + request.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(request.PublicKey, false)); + + using X509Certificate2 certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddDays(30)); + byte[] pfxBytes = certificate.Export(X509ContentType.Pfx, PasswordValue); + byte[] certificateBytes = certificate.Export(X509ContentType.Cert); + + string serverCertificatePath = Path.Combine(certificateDirectory, ServerCertificateFileName); + string rootCertificatePath = Path.Combine(certificateDirectory, RootCertificateFileName); + File.WriteAllBytes(serverCertificatePath, pfxBytes); + File.WriteAllBytes(rootCertificatePath, certificateBytes); + + var result = new IntegrationTestCertificate(certificateDirectory, X509CertificateLoader.LoadPkcs12(pfxBytes, PasswordValue, X509KeyStorageFlags.EphemeralKeySet)); + result.InstallTrustedRoot(); + return result; + } + + public void InstallTrustedRoot() + { + ThrowIfDisposed(); + + using X509Store store = new X509Store(StoreName.TrustedPeople, StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadWrite); + store.Add(_certificate); + _trustedRootInstalled = true; + } + + public string GetContainerCertificatePath() + { + return $"{ContainerCertificateDirectoryPath}/{ServerCertificateFileName}"; + } + + public void Dispose() + { + if (_disposed) + { + return; + } + + if (_trustedRootInstalled) + { + RemoveTrustedRoot(); + } + + if (Directory.Exists(_certificateDirectory)) + { + Directory.Delete(_certificateDirectory, recursive: true); + } + + _certificate.Dispose(); + _disposed = true; + } + + private void RemoveTrustedRoot() + { + using X509Store store = new X509Store(StoreName.TrustedPeople, StoreLocation.CurrentUser); + store.Open(OpenFlags.ReadWrite); + + foreach (X509Certificate2 certificate in store.Certificates.Find(X509FindType.FindByThumbprint, Thumbprint, validOnly: false)) + { + store.Remove(certificate); + } + + _trustedRootInstalled = false; + } + + private void ThrowIfDisposed() + { + ObjectDisposedException.ThrowIf(_disposed, this); + } +} diff --git a/SecurityService.IntegrationTesting.Helpers/SecurityServiceSteps.cs b/SecurityService.IntegrationTesting.Helpers/SecurityServiceSteps.cs index 57497508..774b8e45 100644 --- a/SecurityService.IntegrationTesting.Helpers/SecurityServiceSteps.cs +++ b/SecurityService.IntegrationTesting.Helpers/SecurityServiceSteps.cs @@ -242,11 +242,11 @@ public async Task WhenIGetTheIdentityResourcesIdentityResourceDetailsAreReturned List<(String, String)> roleList = new List<(String, String)>(); foreach (CreateRoleRequest request in requests){ Result? result = await this.SecurityServiceClient.CreateRole(request, cancellationToken).ConfigureAwait(false); - result.IsSuccess.ShouldBeTrue(); + result.IsSuccess.ShouldBeTrue(result.Message); } Result>? roles = await this.SecurityServiceClient.GetRoles(cancellationToken); - roles.IsSuccess.ShouldBeTrue(); + roles.IsSuccess.ShouldBeTrue(roles.Message); foreach (CreateRoleRequest request in requests) { RoleResponse r = roles.Data.Single(r => r.RoleName == request.RoleName); diff --git a/SecurityService.IntegrationTests/Common/DockerHelper.cs b/SecurityService.IntegrationTests/Common/DockerHelper.cs index 37e801f1..0fe6b94d 100644 --- a/SecurityService.IntegrationTests/Common/DockerHelper.cs +++ b/SecurityService.IntegrationTests/Common/DockerHelper.cs @@ -1,32 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Text; +#nullable enable +using DotNet.Testcontainers.Builders; +using DotNet.Testcontainers.Configurations; using Shared.Serialisation; namespace SecurityService.IntergrationTests.Common { using Client; - using Ductus.FluentDocker; - using Ductus.FluentDocker.Builders; - using Ductus.FluentDocker.Commands; - using Ductus.FluentDocker.Common; - using Ductus.FluentDocker.Model.Builders; - using Ductus.FluentDocker.Services; - using Ductus.FluentDocker.Services.Extensions; - using Microsoft.EntityFrameworkCore; + using SecurityService.IntegrationTesting.Helpers; using Shared.IntegrationTesting; - using System.Data.SqlClient; - using System.Linq; + using System; + using System.Collections.Generic; using System.Net; using System.Net.Http; - using System.Net.Security; using System.Security.Cryptography.X509Certificates; - using System.Threading; using System.Threading.Tasks; - + public class DockerHelper : Shared.IntegrationTesting.TestContainers.DockerHelper { - public ISecurityServiceClient SecurityServiceClient; + public ISecurityServiceClient? SecurityServiceClient; + private IntegrationTestCertificate? _integrationTestCertificate; String Serialise(Object arg) { @@ -38,71 +30,111 @@ Object Deserialise(String arg, Type type) return StringSerialiser.DeserializeObject(arg, type, new SerialiserOptions(SerialiserPropertyFormat.SnakeCase)); } - public async Task StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) + public override async Task StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) { - await base.StartContainersForScenarioRun(scenarioName, dockerServices); - - Func securityServiceBaseAddressResolver = api => $"https://localhost:{this.SecurityServicePort}"; - HttpClient httpClient = new HttpClient(); - this.SecurityServiceClient = new SecurityServiceClient(securityServiceBaseAddressResolver,httpClient, Serialise, Deserialise); + this._integrationTestCertificate = IntegrationTestCertificate.Create(); + + try + { + await base.StartContainersForScenarioRun(scenarioName, dockerServices); + + Func securityServiceBaseAddressResolver = _ => $"https://localhost:{this.SecurityServicePort}"; + HttpClient httpClient = this.CreatePinnedHttpClient(); + this.SecurityServiceClient = new SecurityServiceClient(securityServiceBaseAddressResolver, httpClient, Serialise, Deserialise); - ServicePointManager.SecurityProtocol = SecurityProtocolType.SystemDefault; + ServicePointManager.SecurityProtocol = SecurityProtocolType.SystemDefault; + } + catch + { + this.DisposeIntegrationTestCertificate(); + throw; + } } - /*public override DotNet.Testcontainers.Builders.ContainerBuilder SetupSecurityServiceContainer() + public override ContainerBuilder SetupSecurityServiceContainer() { - this.Trace("About to Start Security Container"); - List environmentVariables = this.GetCommonEnvironmentVariables(); - environmentVariables.Add($"ServiceOptions:PublicOrigin=https://{this.SecurityServiceContainerName}:{DockerPorts.SecurityServiceDockerPort}"); - environmentVariables.Add($"ServiceOptions:IssuerUrl=https://{this.SecurityServiceContainerName}:{DockerPorts.SecurityServiceDockerPort}"); - environmentVariables.Add("ASPNETCORE_ENVIRONMENT=IntegrationTest"); - environmentVariables.Add($"urls=https://*:{DockerPorts.SecurityServiceDockerPort}"); - - environmentVariables.Add("ServiceOptions:PasswordOptions:RequiredLength=6"); - environmentVariables.Add("ServiceOptions:PasswordOptions:RequireDigit=false"); - environmentVariables.Add("ServiceOptions:PasswordOptions:RequireUpperCase=false"); - environmentVariables.Add("ServiceOptions:UserOptions:RequireUniqueEmail=false"); - environmentVariables.Add("ServiceOptions:SignInOptions:RequireConfirmedEmail=false"); - - environmentVariables.Add(this.SetConnectionString("ConnectionStrings:PersistedGrantDbContext", $"PersistedGrantStore-{this.TestId}", this.UseSecureSqlServerDatabase)); - environmentVariables.Add(this.SetConnectionString("ConnectionStrings:ConfigurationDbContext", $"Configuration-{this.TestId}", this.UseSecureSqlServerDatabase)); - environmentVariables.Add(this.SetConnectionString("ConnectionStrings:AuthenticationDbContext", $"Authentication-{this.TestId}", this.UseSecureSqlServerDatabase)); + if (this._integrationTestCertificate is null) + { + throw new InvalidOperationException("Integration test certificate was not initialised before container setup."); + } - List additionalEnvironmentVariables = this.GetAdditionalVariables(ContainerType.SecurityService); + Dictionary environmentVariables = this.GetCommonEnvironmentVariables(); + environmentVariables.Add("ServiceOptions:PublicOrigin", $"https://{this.SecurityServiceContainerName}:{DockerPorts.SecurityServiceDockerPort}"); + environmentVariables.Add("ServiceOptions:IssuerUrl", $"https://{this.SecurityServiceContainerName}:{DockerPorts.SecurityServiceDockerPort}"); + environmentVariables.Add("ASPNETCORE_ENVIRONMENT", "IntegrationTest"); + environmentVariables.Add("urls", $"https://*:{DockerPorts.SecurityServiceDockerPort}"); + environmentVariables.Add("ServiceOptions:KestrelOptions:Path", this._integrationTestCertificate.GetContainerCertificatePath()); + environmentVariables.Add("ServiceOptions:KestrelOptions:Password", this._integrationTestCertificate.Password); + environmentVariables.Add("ServiceOptions:PasswordOptions:RequiredLength", "6"); + environmentVariables.Add("ServiceOptions:PasswordOptions:RequireDigit", "false"); + environmentVariables.Add("ServiceOptions:PasswordOptions:RequireUpperCase", "false"); + environmentVariables.Add("ServiceOptions:UserOptions:RequireUniqueEmail", "false"); + environmentVariables.Add("ServiceOptions:SignInOptions:RequireConfirmedEmail", "false"); + environmentVariables.Add("ConnectionStrings:PersistedGrantDbContext", this.SetConnectionString($"PersistedGrantStore-{this.TestId}", this.UseSecureSqlServerDatabase)); + environmentVariables.Add("ConnectionStrings:ConfigurationDbContext", this.SetConnectionString($"Configuration-{this.TestId}", this.UseSecureSqlServerDatabase)); + environmentVariables.Add("ConnectionStrings:AuthenticationDbContext", this.SetConnectionString($"Authentication-{this.TestId}", this.UseSecureSqlServerDatabase)); + Dictionary additionalEnvironmentVariables = this.GetAdditionalVariables(ContainerType.SecurityService); if (additionalEnvironmentVariables != null) { - environmentVariables.AddRange(additionalEnvironmentVariables); + foreach (KeyValuePair additionalEnvironmentVariable in additionalEnvironmentVariables) + { + environmentVariables.Add(additionalEnvironmentVariable.Key, additionalEnvironmentVariable.Value); + } } SimpleResults.Result<(String imageName, Boolean useLatest)> imageDetailsResult = this.GetImageDetails(ContainerType.SecurityService); if (imageDetailsResult.IsFailed) + { throw new Exception($"Image details not found for {ContainerType.SecurityService}"); + } - ContainerBuilder securityServiceContainer = new Builder().UseContainer().WithName(this.SecurityServiceContainerName) - .WithEnvironment(environmentVariables.ToArray()) - .UseImageDetails(imageDetailsResult.Data) - .MountHostFolder(this.DockerPlatform, this.HostTraceFolder) - .SetDockerCredentials(this.DockerCredentials); + ContainerBuilder securityServiceContainer = new ContainerBuilder(imageDetailsResult.Data.imageName) + .WithName(this.SecurityServiceContainerName) + .WithEnvironment(environmentVariables) + .WithBindMount(this._integrationTestCertificate.CertificateDirectory, this._integrationTestCertificate.ContainerCertificateDirectory, AccessMode.ReadOnly); Int32? hostPort = this.GetHostPort(ContainerType.SecurityService); - if (hostPort == null) + if (hostPort is null || hostPort <= 0) { - securityServiceContainer = securityServiceContainer.ExposePort(DockerPorts.SecurityServiceDockerPort); + securityServiceContainer = securityServiceContainer.WithPortBinding(DockerPorts.SecurityServiceDockerPort, true); } else { - securityServiceContainer = securityServiceContainer.ExposePort(hostPort.Value, DockerPorts.SecurityServiceDockerPort); + securityServiceContainer = securityServiceContainer.WithPortBinding(DockerPorts.SecurityServiceDockerPort, hostPort.Value); } - // Now build and return the container return securityServiceContainer; - }*/ - - public override async Task CreateSubscriptions(){ - // No subscriptions needed + } + + public override Task CreateSubscriptions() + { + return Task.CompletedTask; + } + + public void DisposeIntegrationTestCertificate() + { + this._integrationTestCertificate?.Dispose(); + this._integrationTestCertificate = null; + } + + private HttpClient CreatePinnedHttpClient() + { + if (this._integrationTestCertificate is null) + { + throw new InvalidOperationException("Integration test certificate was not initialised before HttpClient creation."); + } + + HttpClientHandler handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (_, certificate, _, _) => + certificate is X509Certificate2 certificate2 && + String.Equals(certificate2.Thumbprint, this._integrationTestCertificate.Thumbprint, StringComparison.OrdinalIgnoreCase) + }; + + return new HttpClient(handler, disposeHandler: true); } } } diff --git a/SecurityService.IntegrationTests/Common/GenericSteps.cs b/SecurityService.IntegrationTests/Common/GenericSteps.cs index cabf07d0..4a144ba8 100644 --- a/SecurityService.IntegrationTests/Common/GenericSteps.cs +++ b/SecurityService.IntegrationTests/Common/GenericSteps.cs @@ -66,7 +66,14 @@ public async Task StartSystem() public async Task StopSystem(){ DockerServices sharedDockerServices = DockerServices.SqlServer; this.TestingContext.Logger.LogInformation("About to Stop Containers for Scenario Run"); - await this.TestingContext.DockerHelper.StopContainersForScenarioRun(sharedDockerServices).ConfigureAwait(false); + try + { + await this.TestingContext.DockerHelper.StopContainersForScenarioRun(sharedDockerServices).ConfigureAwait(false); + } + finally + { + this.TestingContext.DockerHelper.DisposeIntegrationTestCertificate(); + } this.TestingContext.Logger.LogInformation("Containers for Scenario Run Stopped"); } } diff --git a/SecurityService.OpenIdConnect.IntegrationTests/Common/DockerHelper.cs b/SecurityService.OpenIdConnect.IntegrationTests/Common/DockerHelper.cs index ebd68344..a9557036 100644 --- a/SecurityService.OpenIdConnect.IntegrationTests/Common/DockerHelper.cs +++ b/SecurityService.OpenIdConnect.IntegrationTests/Common/DockerHelper.cs @@ -9,6 +9,7 @@ namespace SecurityService.IntergrationTests.Common { using Client; + using SecurityService.IntegrationTesting.Helpers; using Shared.HealthChecks; using Shared.IntegrationTesting; using Shared.Logger; @@ -21,6 +22,7 @@ namespace SecurityService.IntergrationTests.Common using System.Linq; using System.Net.Http; using System.Runtime.CompilerServices; + using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; @@ -36,6 +38,8 @@ public class DockerHelper : Shared.IntegrationTesting.TestContainers.DockerHelpe /// The security service client /// public ISecurityServiceClient SecurityServiceClient; + public HttpClient httpClient = new HttpClient(); + private IntegrationTestCertificate? _integrationTestCertificate; /// /// The security service test UI container name @@ -65,7 +69,6 @@ public DockerHelper() : base() #region Methods public Func securityServiceBaseAddressResolver; - public HttpClient httpClient = new HttpClient(); public override void SetupContainerNames() { @@ -97,17 +100,27 @@ Object Deserialise(String arg, Type type) public override async Task StartContainersForScenarioRun(String scenarioName, DockerServices dockerServices) { this.Trace($"Test Id is {this.TestId} and Scenario {scenarioName}"); + this._integrationTestCertificate = IntegrationTestCertificate.Create(); - await base.StartContainersForScenarioRun(scenarioName,dockerServices); + try + { + await base.StartContainersForScenarioRun(scenarioName,dockerServices); - await StartContainer(SetupSecurityServiceTestUIContainer, this.TestNetworks); + await StartContainer(SetupSecurityServiceTestUIContainer, this.TestNetworks); - securityServiceBaseAddressResolver = api => $"https://localhost:{this.SecurityServicePort}"; + securityServiceBaseAddressResolver = api => $"https://localhost:{this.SecurityServicePort}"; - this.SecurityServiceClient = new SecurityServiceClient(securityServiceBaseAddressResolver, httpClient, Serialise, Deserialise); + this.httpClient = this.CreatePinnedHttpClient(); + this.SecurityServiceClient = new SecurityServiceClient(securityServiceBaseAddressResolver, this.httpClient, Serialise, Deserialise); - DockerHelper.AddEntryToHostsFile("127.0.0.1", SecurityServiceContainerName); - DockerHelper.AddEntryToHostsFile("localhost", SecurityServiceContainerName); + DockerHelper.AddEntryToHostsFile("127.0.0.1", SecurityServiceContainerName); + DockerHelper.AddEntryToHostsFile("localhost", SecurityServiceContainerName); + } + catch + { + this.DisposeIntegrationTestCertificate(); + throw; + } } protected async Task DoTestUIHealthCheck() @@ -221,8 +234,10 @@ private ContainerBuilder SetupSecurityServiceTestUIContainer(){ - ContainerBuilder securityServiceTestUIContainer = new ContainerBuilder().WithName(this.SecurityServiceTestUIContainerName) - .WithEnvironment(environmentVariables).WithImage("securityservicetestui").WithPortBinding(5004, true); + ContainerBuilder securityServiceTestUIContainer = new ContainerBuilder("securityservicetestui") + .WithName(this.SecurityServiceTestUIContainerName) + .WithEnvironment(environmentVariables) + .WithPortBinding(5004, true); return securityServiceTestUIContainer; } @@ -231,11 +246,18 @@ public override ContainerBuilder SetupSecurityServiceContainer() { this.Trace("About to Start Security Container"); + if (this._integrationTestCertificate is null) + { + throw new InvalidOperationException("Integration test certificate was not initialised before container setup."); + } + var environmentVariables = this.GetCommonEnvironmentVariables(); environmentVariables.Add($"ServiceOptions:PublicOrigin",$"https://{this.SecurityServiceContainerName}:{DockerPorts.SecurityServiceDockerPort}"); environmentVariables.Add($"ServiceOptions:IssuerUrl",$"https://{this.SecurityServiceContainerName}:{DockerPorts.SecurityServiceDockerPort}"); environmentVariables.Add("ASPNETCORE_ENVIRONMENT","IntegrationTest"); environmentVariables.Add($"urls",$"https://*:{DockerPorts.SecurityServiceDockerPort}"); + environmentVariables.Add("ServiceOptions:KestrelOptions:Path", this._integrationTestCertificate.GetContainerCertificatePath()); + environmentVariables.Add("ServiceOptions:KestrelOptions:Password", this._integrationTestCertificate.Password); environmentVariables.Add($"ServiceOptions:PasswordOptions:RequiredLength","6"); environmentVariables.Add($"ServiceOptions:PasswordOptions:RequireDigit","false"); @@ -260,11 +282,39 @@ public override ContainerBuilder SetupSecurityServiceContainer() if (imageDetailsResult.IsFailed) throw new Exception($"Image details not found for {ContainerType.SecurityService}"); - ContainerBuilder securityServiceContainer = new ContainerBuilder().WithName(this.SecurityServiceContainerName).WithEnvironment(environmentVariables).WithImage(imageDetailsResult.Data.imageName).WithPortBinding(DockerPorts.SecurityServiceDockerPort, DockerPorts.SecurityServiceDockerPort).MountHostFolder(this.DockerPlatform, this.HostTraceFolder); + ContainerBuilder securityServiceContainer = new ContainerBuilder(imageDetailsResult.Data.imageName) + .WithName(this.SecurityServiceContainerName) + .WithEnvironment(environmentVariables) + .WithPortBinding(DockerPorts.SecurityServiceDockerPort, DockerPorts.SecurityServiceDockerPort) + .WithBindMount(this._integrationTestCertificate.CertificateDirectory, this._integrationTestCertificate.ContainerCertificateDirectory, AccessMode.ReadOnly) + .MountHostFolder(this.DockerPlatform, this.HostTraceFolder); return securityServiceContainer; } + public void DisposeIntegrationTestCertificate() + { + this._integrationTestCertificate?.Dispose(); + this._integrationTestCertificate = null; + } + + private HttpClient CreatePinnedHttpClient() + { + if (this._integrationTestCertificate is null) + { + throw new InvalidOperationException("Integration test certificate was not initialised before HttpClient creation."); + } + + HttpClientHandler handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (_, certificate, _, _) => + certificate is X509Certificate2 certificate2 && + String.Equals(certificate2.Thumbprint, this._integrationTestCertificate.Thumbprint, StringComparison.OrdinalIgnoreCase) + }; + + return new HttpClient(handler, disposeHandler: true); + } + #endregion } -} \ No newline at end of file +} diff --git a/SecurityService.OpenIdConnect.IntegrationTests/Common/GenericSteps.cs b/SecurityService.OpenIdConnect.IntegrationTests/Common/GenericSteps.cs index 472bb5cd..5d779730 100644 --- a/SecurityService.OpenIdConnect.IntegrationTests/Common/GenericSteps.cs +++ b/SecurityService.OpenIdConnect.IntegrationTests/Common/GenericSteps.cs @@ -60,7 +60,14 @@ public async Task StartSystem() public async Task StopSystem(){ DockerServices sharedDockerServices = DockerServices.None; this.TestingContext.DockerHelper.Logger.LogInformation("About to Stop Containers for Scenario Run"); - await this.TestingContext.DockerHelper.StopContainersForScenarioRun(sharedDockerServices).ConfigureAwait(false); + try + { + await this.TestingContext.DockerHelper.StopContainersForScenarioRun(sharedDockerServices).ConfigureAwait(false); + } + finally + { + this.TestingContext.DockerHelper.DisposeIntegrationTestCertificate(); + } this.TestingContext.DockerHelper.Logger.LogInformation("Containers for Scenario Run Stopped"); } }