Add Custom Certificate settings (#537)

This commit is contained in:
Stef Heyenrath
2020-11-10 15:40:15 +00:00
committed by GitHub
parent a0fdc002c8
commit 09533f1e3a
24 changed files with 478 additions and 103 deletions

View File

@@ -71,6 +71,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.WebApplication
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.WebApplication.NETCore3", "examples\WireMock.Net.WebApplication.NETCore3\WireMock.Net.WebApplication.NETCore3.csproj", "{E1C56967-3DC7-46CB-A1DF-B13167A0D9D4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.NETCoreApp3WithCertificate", "examples\WireMock.Net.Console.NETCoreApp3WithCertificate\WireMock.Net.Console.NETCoreApp3WithCertificate.csproj", "{925E421A-1B3F-4202-B48F-734743573A4B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -173,6 +175,10 @@ Global
{E1C56967-3DC7-46CB-A1DF-B13167A0D9D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E1C56967-3DC7-46CB-A1DF-B13167A0D9D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E1C56967-3DC7-46CB-A1DF-B13167A0D9D4}.Release|Any CPU.Build.0 = Release|Any CPU
{925E421A-1B3F-4202-B48F-734743573A4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{925E421A-1B3F-4202-B48F-734743573A4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{925E421A-1B3F-4202-B48F-734743573A4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{925E421A-1B3F-4202-B48F-734743573A4B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -202,6 +208,7 @@ Global
{B6269AAC-170A-4346-8B9A-579DED3D9A95} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{6F38CB3A-6DA1-408A-AECD-E434523C2838} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{E1C56967-3DC7-46CB-A1DF-B13167A0D9D4} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{925E421A-1B3F-4202-B48F-734743573A4B} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}

View File

@@ -0,0 +1,36 @@
using WireMock.Logging;
using WireMock.Server;
using WireMock.Settings;
namespace WireMock.Net.Console.NETCoreApp3WithCertificate
{
class Program
{
static void Main(string[] args)
{
string url = "https://localhost:8433/";
var server = WireMockServer.Start(new WireMockServerSettings
{
Urls = new[] { url },
StartAdminInterface = true,
Logger = new WireMockConsoleLogger(),
CertificateSettings = new WireMockCertificateSettings
{
X509StoreName = "My",
X509StoreLocation = "CurrentUser",
X509StoreThumbprintOrSubjectName = "FE16586076A8B3F3E2F1466803A6C4C7CA35455B"
// X509CertificateFilePath = "example.pfx",
// X509CertificatePassword = "wiremock"
}
});
System.Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls));
System.Console.WriteLine("Press any key to stop the server");
System.Console.ReadKey();
server.Stop();
}
}
}

View File

@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="example.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEsDCCApigAwIBAgIQJbH6hSGKdoFI0B7qCIOK7jANBgkqhkiG9w0BAQUFADAU
MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAxMDMwMjMwMDAwWhcNMzAxMTA2MjMw
MDAwWjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQCl5fQSrRgT3Q6WoULR98Y+rrDWtTTgVpbLU04G0hLZ4yUeP7Wa
yuVbvx7zX8XT4lA8Hu5T/GG91U077JcSSEjnPBFsh4hE7FkRoSYIEW6BFG7D7eUG
dGHnDV8UkSRQ97LJPyjXuHVDJzNDJ9xQGMzOZ4n8vQ7SEKBw9hRG2ugkP5b2jVIN
e1E549tq2jnIVpKCZ4+prf64ZLsaokX7VHe+b/CW3GoAqUUaUjdTpAQ7LpypJuFz
415enOrKQe+UEBdqhGlgcC/O/Bw0uq4qVk+NNe5DEINVwoYs9XjNdzxuIkkAtcCt
avTEzhHf8zWYLb5Nt2DIOcRGVELvRhsBX4um5f7dOGzMbXzBfUdjkP2O4hi6crhm
Hba5bNkj4Zw2EHR9Xua3nadGCj22z0vpMKP2gXdFVnxFqQlaUWBLtwwN9p6tCQHl
kU7wypvOHUsMa2Ojg5eZP4RpYFvZG3kkc9zTZCSakgw2n0ampBbvxPP11/AYIXtz
HKu3CKcpjVQ+lE0DAU/Mm77QJ24TMbXmAydwCf1UCdFbDUZhdM9lspHvA0J9eiCv
LOE94BrpVKuZ6TrAW0LZjAmBnkqYQAewhTW7GSgARE+QQcwfyu03Ck7id3Zt4FeQ
sQDo0NNj7zQOy3Y1GK0ZYAVZv/GUeHMkxpClSWPoub/f5SJ4YzD5Il0cQQIDAQAB
MA0GCSqGSIb3DQEBBQUAA4ICAQBd91xfUepnWcKwmupie2h1CAAQZEunyW78i++t
evABfBu0TgV4s6Xe0umFv9V4r+O+rrF3ddSudbSOPBEb0Ooe+e3YGlNk1JrI1EEn
fhb0YI8bMfBNpl85yNqxgByra7JF2mG4qbAnjrCs/PZkXo/34N29SY6dyZ7mffR3
r/l01Rdm3ogRwGkiMUeKb3iGwLUy1T55svuI3Zc13N+NJT1s9NqpwWeK/jFK/WRN
5Hi9W3DmlGCYAwFPCyBaQagxpGuGIpNsU0hKp86W5EvJpBpmCihfwlydH8ZbkHJ9
jx2UDgTCaDzmaiKysiTP2HHDBsReL4tjakBksa9jkTfy5ajB53F3aUVs4jvTA46L
w8wcAJlRPBz5siBrv4CH/0lBMyNeYzuqmDY3ulF4IMKNb5Kk9Ye4Pt0474z50A4v
fSah+9iwI/mubaJ5tK522AtWtUoOIAswIwpDQyNeJPOggyzT2Y2OYZdGuFAoMYuq
ZD58k4Yo+vky9K88l8NuzNJJvtgTKtT+/9qfMucxFmnvwbKEEULP3sw1FUKkPtM4
f242FIV/XnOeloDmhGGeTB7aODB+gGCvgmOH92njjUEIv+SnYQkflQaRhhyNIACi
ZvWlP/96H+X4fUG5kVNBHY021ZWmurUDqVxWUaswg63+DfsZcYtt6wgxiAN4ssXG
wLnLPw==
-----END CERTIFICATE-----

View File

@@ -41,7 +41,7 @@ namespace WireMock.Http
{
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
var x509Certificate2 = ClientCertificateHelper.GetCertificate(settings.ClientX509Certificate2ThumbprintOrSubjectName);
var x509Certificate2 = CertificateLoader.LoadCertificate(settings.ClientX509Certificate2ThumbprintOrSubjectName);
handler.ClientCertificates.Add(x509Certificate2);
}

View File

@@ -0,0 +1,100 @@
using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
namespace WireMock.HttpsCertificate
{
internal static class CertificateLoader
{
/// <summary>
/// Used by the WireMock.Net server
/// </summary>
public static X509Certificate2 LoadCertificate(
string storeName,
string storeLocation,
string thumbprintOrSubjectName,
string filePath,
string password,
string host)
{
if (!string.IsNullOrEmpty(storeName) && !string.IsNullOrEmpty(storeLocation))
{
var thumbprintOrSubjectNameOrHost = thumbprintOrSubjectName ?? host;
var certStore = new X509Store((StoreName)Enum.Parse(typeof(StoreName), storeName), (StoreLocation)Enum.Parse(typeof(StoreLocation), storeLocation));
try
{
certStore.Open(OpenFlags.ReadOnly);
// Attempt to find by Thumbprint first
var matchingCertificates = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprintOrSubjectNameOrHost, false);
if (matchingCertificates.Count == 0)
{
// Fallback to SubjectName
matchingCertificates = certStore.Certificates.Find(X509FindType.FindBySubjectName, thumbprintOrSubjectNameOrHost, false);
if (matchingCertificates.Count == 0)
{
// No certificates matched the search criteria.
throw new FileNotFoundException($"No Certificate found with in store '{storeName}', location '{storeLocation}' for Thumbprint or SubjectName '{thumbprintOrSubjectNameOrHost}'.");
}
}
// Use the first matching certificate.
return matchingCertificates[0];
}
finally
{
#if NETSTANDARD || NET46
certStore.Dispose();
#else
certStore.Close();
#endif
}
}
if (!string.IsNullOrEmpty(filePath) && !string.IsNullOrEmpty(password))
{
return new X509Certificate2(filePath, password);
}
throw new InvalidOperationException("X509StoreName and X509StoreLocation OR X509CertificateFilePath and X509CertificatePassword are mandatory.");
}
/// <summary>
/// Used for Proxy
/// </summary>
public static X509Certificate2 LoadCertificate(string thumbprintOrSubjectName)
{
var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
// Certificate must be in the local machine store
certStore.Open(OpenFlags.ReadOnly);
// Attempt to find by Thumbprint first
var matchingCertificates = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprintOrSubjectName, false);
if (matchingCertificates.Count == 0)
{
// Fallback to SubjectName
matchingCertificates = certStore.Certificates.Find(X509FindType.FindBySubjectName, thumbprintOrSubjectName, false);
if (matchingCertificates.Count == 0)
{
// No certificates matched the search criteria.
throw new FileNotFoundException("No certificate found with specified Thumbprint or SubjectName.", thumbprintOrSubjectName);
}
}
// Use the first matching certificate.
return matchingCertificates[0];
}
finally
{
#if NETSTANDARD || NET46
certStore.Dispose();
#else
certStore.Close();
#endif
}
}
}
}

View File

@@ -1,42 +0,0 @@
using System.IO;
using System.Security.Cryptography.X509Certificates;
namespace WireMock.HttpsCertificate
{
internal static class ClientCertificateHelper
{
public static X509Certificate2 GetCertificate(string thumbprintOrSubjectName)
{
X509Store certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
// Certificate must be in the local machine store
certStore.Open(OpenFlags.ReadOnly);
// Attempt to find by thumbprint first
var matchingCertificates = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprintOrSubjectName, false);
if (matchingCertificates.Count == 0)
{
// Fallback to subject name
matchingCertificates = certStore.Certificates.Find(X509FindType.FindBySubjectName, thumbprintOrSubjectName, false);
if (matchingCertificates.Count == 0)
{
// No certificates matched the search criteria.
throw new FileNotFoundException("No certificate found with specified Thumbprint or SubjectName.", thumbprintOrSubjectName);
}
}
// Use the first matching certificate.
return matchingCertificates[0];
}
finally
{
#if NETSTANDARD || NET46
certStore.Dispose();
#else
certStore.Close();
#endif
}
}
}
}

View File

@@ -1,10 +1,10 @@
#if USE_ASPNETCORE && !NETSTANDARD1_3
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using WireMock.HttpsCertificate;
namespace WireMock.Owin
{
@@ -18,20 +18,34 @@ namespace WireMock.Owin
options.Limits.MaxResponseBufferSize = null;
}
private static void SetHttpsAndUrls(KestrelServerOptions options, ICollection<(string Url, int Port)> urlDetails)
private static void SetHttpsAndUrls(KestrelServerOptions kestrelOptions, IWireMockMiddlewareOptions wireMockMiddlewareOptions, IEnumerable<HostUrlDetails> urlDetails)
{
foreach (var detail in urlDetails)
foreach (var urlDetail in urlDetails)
{
if (detail.Url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
if (urlDetail.IsHttps)
{
options.Listen(System.Net.IPAddress.Any, detail.Port, listenOptions =>
kestrelOptions.Listen(System.Net.IPAddress.Any, urlDetail.Port, listenOptions =>
{
listenOptions.UseHttps();
if (wireMockMiddlewareOptions.CustomCertificateDefined)
{
listenOptions.UseHttps(CertificateLoader.LoadCertificate(
wireMockMiddlewareOptions.X509StoreName,
wireMockMiddlewareOptions.X509StoreLocation,
wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
wireMockMiddlewareOptions.X509CertificateFilePath,
wireMockMiddlewareOptions.X509CertificatePassword,
urlDetail.Host)
);
}
else
{
listenOptions.UseHttps();
}
});
}
else
{
options.Listen(System.Net.IPAddress.Any, detail.Port);
kestrelOptions.Listen(System.Net.IPAddress.Any, urlDetail.Port);
}
}
}

View File

@@ -1,7 +1,5 @@
#if USE_ASPNETCORE && NETSTANDARD1_3
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel;
using Microsoft.Extensions.Configuration;
@@ -19,12 +17,28 @@ namespace WireMock.Owin
options.Limits.MaxResponseBufferSize = null;
}
private static void SetHttpsAndUrls(KestrelServerOptions options, ICollection<(string Url, int Port)> urlDetails)
private static void SetHttpsAndUrls(KestrelServerOptions options, IWireMockMiddlewareOptions wireMockMiddlewareOptions, IEnumerable<HostUrlDetails> urlDetails)
{
var urls = urlDetails.Select(u => u.Url);
if (urls.Any(u => u.StartsWith("https://", StringComparison.OrdinalIgnoreCase)))
foreach (var urlDetail in urlDetails)
{
options.UseHttps(PublicCertificateHelper.GetX509Certificate2());
if (urlDetail.IsHttps)
{
if (wireMockMiddlewareOptions.CustomCertificateDefined)
{
options.UseHttps(CertificateLoader.LoadCertificate(
wireMockMiddlewareOptions.X509StoreName,
wireMockMiddlewareOptions.X509StoreLocation,
wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
wireMockMiddlewareOptions.X509CertificateFilePath,
wireMockMiddlewareOptions.X509CertificatePassword,
urlDetail.Host)
);
}
else
{
options.UseHttps(PublicCertificateHelper.GetX509Certificate2());
}
}
}
}
}

View File

@@ -18,7 +18,7 @@ namespace WireMock.Owin
internal partial class AspNetCoreSelfHost : IOwinSelfHost
{
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
private readonly IWireMockMiddlewareOptions _options;
private readonly IWireMockMiddlewareOptions _wireMockMiddlewareOptions;
private readonly IWireMockLogger _logger;
private readonly HostUrlOptions _urlOptions;
@@ -33,14 +33,14 @@ namespace WireMock.Owin
public Exception RunningException => _runningException;
public AspNetCoreSelfHost([NotNull] IWireMockMiddlewareOptions options, [NotNull] HostUrlOptions urlOptions)
public AspNetCoreSelfHost([NotNull] IWireMockMiddlewareOptions wireMockMiddlewareOptions, [NotNull] HostUrlOptions urlOptions)
{
Check.NotNull(options, nameof(options));
Check.NotNull(wireMockMiddlewareOptions, nameof(wireMockMiddlewareOptions));
Check.NotNull(urlOptions, nameof(urlOptions));
_logger = options.Logger ?? new WireMockConsoleLogger();
_logger = wireMockMiddlewareOptions.Logger ?? new WireMockConsoleLogger();
_options = options;
_wireMockMiddlewareOptions = wireMockMiddlewareOptions;
_urlOptions = urlOptions;
}
@@ -61,7 +61,7 @@ namespace WireMock.Owin
.ConfigureAppConfigurationUsingEnvironmentVariables()
.ConfigureServices(services =>
{
services.AddSingleton(_options);
services.AddSingleton(_wireMockMiddlewareOptions);
services.AddSingleton<IMappingMatcher, MappingMatcher>();
services.AddSingleton<IOwinRequestMapper, OwinRequestMapper>();
services.AddSingleton<IOwinResponseMapper, OwinResponseMapper>();
@@ -70,17 +70,17 @@ namespace WireMock.Owin
{
appBuilder.UseMiddleware<GlobalExceptionMiddleware>();
_options.PreWireMockMiddlewareInit?.Invoke(appBuilder);
_wireMockMiddlewareOptions.PreWireMockMiddlewareInit?.Invoke(appBuilder);
appBuilder.UseMiddleware<WireMockMiddleware>();
_options.PostWireMockMiddlewareInit?.Invoke(appBuilder);
_wireMockMiddlewareOptions.PostWireMockMiddlewareInit?.Invoke(appBuilder);
})
.UseKestrel(options =>
{
SetKestrelOptionsLimits(options);
SetHttpsAndUrls(options, _urlOptions.GetDetails());
SetHttpsAndUrls(options, _wireMockMiddlewareOptions, _urlOptions.GetDetails());
})
.ConfigureKestrelServerOptions()
@@ -107,7 +107,7 @@ namespace WireMock.Owin
{
Urls.Add(address.Replace("0.0.0.0", "localhost"));
PortUtils.TryExtract(address, out string protocol, out string host, out int port);
PortUtils.TryExtract(address, out bool isHttps, out string protocol, out string host, out int port);
Ports.Add(port);
}

View File

@@ -0,0 +1,15 @@
namespace WireMock.Owin
{
internal class HostUrlDetails
{
public bool IsHttps { get; set; }
public string Url { get; set; }
public string Protocol { get; set; }
public string Host { get; set; }
public int Port { get; set; }
}
}

View File

@@ -5,26 +5,29 @@ namespace WireMock.Owin
{
internal class HostUrlOptions
{
private const string LOCALHOST = "localhost";
public ICollection<string> Urls { get; set; }
public int? Port { get; set; }
public bool UseSSL { get; set; }
public ICollection<(string Url, int Port)> GetDetails()
public ICollection<HostUrlDetails> GetDetails()
{
var list = new List<(string Url, int Port)>();
var list = new List<HostUrlDetails>();
if (Urls == null)
{
int port = Port > 0 ? Port.Value : FindFreeTcpPort();
list.Add(($"{(UseSSL ? "https" : "http")}://localhost:{port}", port));
string protocol = UseSSL ? "https" : "http";
list.Add(new HostUrlDetails { IsHttps = UseSSL, Url = $"{protocol}://{LOCALHOST}:{port}", Protocol = protocol, Host = LOCALHOST, Port = port });
}
else
{
foreach (string url in Urls)
{
PortUtils.TryExtract(url, out string protocol, out string host, out int port);
list.Add((url, port));
PortUtils.TryExtract(url, out bool isHttps, out string protocol, out string host, out int port);
list.Add(new HostUrlDetails { IsHttps = isHttps, Url = url, Protocol = protocol, Host = host, Port = port });
}
}

View File

@@ -47,5 +47,17 @@ namespace WireMock.Owin
bool? DisableRequestBodyDecompressing { get; set; }
bool? HandleRequestsSynchronously { get; set; }
string X509StoreName { get; set; }
string X509StoreLocation { get; set; }
string X509ThumbprintOrSubjectName { get; set; }
string X509CertificateFilePath { get; set; }
string X509CertificatePassword { get; set; }
bool CustomCertificateDefined { get; }
}
}

View File

@@ -53,5 +53,25 @@ namespace WireMock.Owin
/// <inheritdoc cref="IWireMockMiddlewareOptions.HandleRequestsSynchronously"/>
public bool? HandleRequestsSynchronously { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509StoreName"/>
public string X509StoreName { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509StoreLocation"/>
public string X509StoreLocation { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509ThumbprintOrSubjectName"/>
public string X509ThumbprintOrSubjectName { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509CertificateFilePath"/>
public string X509CertificateFilePath { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509CertificatePassword"/>
public string X509CertificatePassword { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.CustomCertificateDefined"/>
public bool CustomCertificateDefined =>
!string.IsNullOrEmpty(X509StoreName) && !string.IsNullOrEmpty(X509StoreLocation) ||
!string.IsNullOrEmpty(X509CertificateFilePath) && !string.IsNullOrEmpty(X509CertificatePassword);
}
}

View File

@@ -230,6 +230,15 @@ namespace WireMock.Server
_options.DisableJsonBodyParsing = _settings.DisableJsonBodyParsing;
_options.HandleRequestsSynchronously = settings.HandleRequestsSynchronously;
if (settings.CustomCertificateDefined)
{
_options.X509StoreName = settings.CertificateSettings.X509StoreName;
_options.X509StoreLocation = settings.CertificateSettings.X509StoreLocation;
_options.X509ThumbprintOrSubjectName = settings.CertificateSettings.X509StoreThumbprintOrSubjectName;
_options.X509CertificateFilePath = settings.CertificateSettings.X509CertificateFilePath;
_options.X509CertificatePassword = settings.CertificateSettings.X509CertificatePassword;
}
_matcherMapper = new MatcherMapper(_settings);
_mappingConverter = new MappingConverter(_matcherMapper);

View File

@@ -0,0 +1,44 @@
namespace WireMock.Settings
{
/// <summary>
/// If https is used, these settings can be used to configure the CertificateSettings in case a custom certificate instead the default .NET certificate should be used.
///
/// X509StoreName and X509StoreLocation should be defined
/// OR
/// X509CertificateFilePath and X509CertificatePassword should be defined
/// </summary>
public interface IWireMockCertificateSettings
{
/// <summary>
/// X509 StoreName (AddressBook, AuthRoot, CertificateAuthority, My, Root, TrustedPeople or TrustedPublisher)
/// </summary>
string X509StoreName { get; set; }
/// <summary>
/// X509 StoreLocation (CurrentUser or LocalMachine)
/// </summary>
string X509StoreLocation { get; set; }
/// <summary>
/// X509 Thumbprint or SubjectName (if not defined, the 'host' is used)
/// </summary>
string X509StoreThumbprintOrSubjectName { get; set; }
/// <summary>
/// X509Certificate FilePath
/// </summary>
string X509CertificateFilePath { get; set; }
/// <summary>
/// X509Certificate Password
/// </summary>
string X509CertificatePassword { get; set; }
/// <summary>
/// X509StoreName and X509StoreLocation should be defined
/// OR
/// X509CertificateFilePath and X509CertificatePassword should be defined
/// </summary>
bool IsDefined { get; }
}
}

View File

@@ -170,5 +170,21 @@ namespace WireMock.Settings
/// </summary>
[PublicAPI]
bool? ThrowExceptionWhenMatcherFails { get; set; }
/// <summary>
/// If https is used, these settings can be used to configure the CertificateSettings in case a custom certificate instead the default .NET certificate should be used.
///
/// X509StoreName and X509StoreLocation should be defined
/// OR
/// X509CertificateFilePath and X509CertificatePassword should be defined
/// </summary>
[PublicAPI]
IWireMockCertificateSettings CertificateSettings { get; set; }
/// <summary>
/// Defines if custom CertificateSettings are defined
/// </summary>
[PublicAPI]
bool CustomCertificateDefined { get; }
}
}

View File

@@ -0,0 +1,36 @@
using JetBrains.Annotations;
namespace WireMock.Settings
{
/// <summary>
/// <see cref="IWireMockCertificateSettings"/>
/// </summary>
public class WireMockCertificateSettings : IWireMockCertificateSettings
{
/// <inheritdoc cref="IWireMockCertificateSettings.X509StoreName"/>
[PublicAPI]
public string X509StoreName { get; set; }
/// <inheritdoc cref="IWireMockCertificateSettings.X509StoreLocation"/>
[PublicAPI]
public string X509StoreLocation { get; set; }
/// <inheritdoc cref="IWireMockCertificateSettings.X509StoreThumbprintOrSubjectName"/>
[PublicAPI]
public string X509StoreThumbprintOrSubjectName { get; set; }
/// <inheritdoc cref="IWireMockCertificateSettings.X509CertificateFilePath"/>
[PublicAPI]
public string X509CertificateFilePath { get; set; }
/// <inheritdoc cref="IWireMockCertificateSettings.X509CertificatePassword"/>
[PublicAPI]
public string X509CertificatePassword { get; set; }
/// <inheritdoc cref="IWireMockCertificateSettings.IsDefined"/>
[PublicAPI]
public bool IsDefined =>
!string.IsNullOrEmpty(X509StoreName) && !string.IsNullOrEmpty(X509StoreLocation) ||
!string.IsNullOrEmpty(X509CertificateFilePath) && !string.IsNullOrEmpty(X509CertificatePassword);
}
}

View File

@@ -1,6 +1,6 @@
using HandlebarsDotNet;
using System;
using HandlebarsDotNet;
using JetBrains.Annotations;
using System;
using Newtonsoft.Json;
using WireMock.Handlers;
using WireMock.Logging;
@@ -121,5 +121,13 @@ namespace WireMock.Settings
/// <inheritdoc cref="IWireMockServerSettings.ThrowExceptionWhenMatcherFails"/>
[PublicAPI]
public bool? ThrowExceptionWhenMatcherFails { get; set; }
/// <inheritdoc cref="IWireMockServerSettings.CertificateSettings"/>
[PublicAPI]
public IWireMockCertificateSettings CertificateSettings { get; set; }
/// <inheritdoc cref="IWireMockServerSettings.CustomCertificateDefined"/>
[PublicAPI]
public bool CustomCertificateDefined => CertificateSettings?.IsDefined == true;
}
}

View File

@@ -60,12 +60,12 @@ namespace WireMock.Settings
settings.Urls = parser.GetValues("Urls", new[] { "http://*:9091/" });
}
string proxyURL = parser.GetStringValue("ProxyURL");
if (!string.IsNullOrEmpty(proxyURL))
string proxyUrl = parser.GetStringValue("ProxyURL") ?? parser.GetStringValue("ProxyUrl");
if (!string.IsNullOrEmpty(proxyUrl))
{
settings.ProxyAndRecordSettings = new ProxyAndRecordSettings
{
Url = proxyURL,
Url = proxyUrl,
SaveMapping = parser.GetBoolValue("SaveMapping"),
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
SaveMappingForStatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern"),
@@ -87,6 +87,19 @@ namespace WireMock.Settings
}
}
var certificateSettings = new WireMockCertificateSettings
{
X509StoreName = parser.GetStringValue("X509StoreName"),
X509StoreLocation = parser.GetStringValue("X509StoreLocation"),
X509StoreThumbprintOrSubjectName = parser.GetStringValue("X509StoreThumbprintOrSubjectName"),
X509CertificateFilePath = parser.GetStringValue("X509CertificateFilePath"),
X509CertificatePassword = parser.GetStringValue("X509CertificatePassword")
};
if (certificateSettings.IsDefined)
{
settings.CertificateSettings = certificateSettings;
}
return settings;
}
}

View File

@@ -1,4 +1,5 @@
using System.Net;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
@@ -32,21 +33,23 @@ namespace WireMock.Util
}
/// <summary>
/// Extract the protocol, host and port from a URL.
/// Extract the if-isHttps, protocol, host and port from a URL.
/// </summary>
public static bool TryExtract(string url, out string protocol, out string host, out int port)
public static bool TryExtract(string url, out bool isHttps, out string protocol, out string host, out int port)
{
isHttps = false;
protocol = null;
host = null;
port = default(int);
port = default;
Match m = UrlDetailsRegex.Match(url);
if (m.Success)
var match = UrlDetailsRegex.Match(url);
if (match.Success)
{
protocol = m.Groups["proto"].Value;
host = m.Groups["host"].Value;
protocol = match.Groups["proto"].Value;
isHttps = protocol.StartsWith("https", StringComparison.OrdinalIgnoreCase);
host = match.Groups["host"].Value;
return int.TryParse(m.Groups["port"].Value, out port);
return int.TryParse(match.Groups["port"].Value, out port);
}
return false;

View File

@@ -1,3 +1,4 @@
using FluentAssertions;
using NFluent;
using WireMock.Util;
using Xunit;
@@ -13,13 +14,14 @@ namespace WireMock.Net.Tests.Util
string url = "test";
// Act
bool result = PortUtils.TryExtract(url, out string proto, out string host, out int port);
bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
// Assert
Check.That(result).IsFalse();
Check.That(proto).IsNull();
Check.That(host).IsNull();
Check.That(port).IsEqualTo(default(int));
result.Should().BeFalse();
isHttps.Should().BeFalse();
proto.Should().BeNull();
host.Should().BeNull();
port.Should().Be(default(int));
}
[Fact]
@@ -29,39 +31,58 @@ namespace WireMock.Net.Tests.Util
string url = "http://0.0.0.0";
// Act
bool result = PortUtils.TryExtract(url, out string proto, out string host, out int port);
bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
// Assert
Check.That(result).IsFalse();
Check.That(proto).IsNull();
Check.That(host).IsNull();
Check.That(port).IsEqualTo(default(int));
result.Should().BeFalse();
isHttps.Should().BeFalse();
proto.Should().BeNull();
host.Should().BeNull();
port.Should().Be(default(int));
}
[Fact]
public void PortUtils_TryExtract_ValidUrl1_Returns_True()
public void PortUtils_TryExtract_Http_Returns_True()
{
// Assign
string url = "http://wiremock.net:1234";
// Act
bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
// Assert
result.Should().BeTrue();
isHttps.Should().BeFalse();
proto.Should().Be("http");
host.Should().Be("wiremock.net");
port.Should().Be(1234);
}
[Fact]
public void PortUtils_TryExtract_Https_Returns_True()
{
// Assign
string url = "https://wiremock.net:5000";
// Act
bool result = PortUtils.TryExtract(url, out string proto, out string host, out int port);
bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
// Assert
Check.That(result).IsTrue();
Check.That(proto).IsEqualTo("https");
Check.That(host).IsEqualTo("wiremock.net");
Check.That(port).IsEqualTo(5000);
result.Should().BeTrue();
isHttps.Should().BeTrue();
proto.Should().Be("https");
host.Should().Be("wiremock.net");
port.Should().Be(5000);
}
[Fact]
public void PortUtils_TryExtract_ValidUrl2_Returns_True()
public void PortUtils_TryExtract_Https0_0_0_0_Returns_True()
{
// Assign
string url = "https://0.0.0.0:5000";
// Act
bool result = PortUtils.TryExtract(url, out string proto, out string host, out int port);
bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
// Assert
Check.That(result).IsTrue();