From 9c81c1c5dc7898d7eae1ac2589c68772399408bf Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Thu, 14 Sep 2023 11:15:46 -0700 Subject: [PATCH 1/8] Change how ReCaptchaClient is registered --- .../ServiceCollectionExtensions.cs | 9 ++++++--- .../Services/ReCaptchaClient.cs | 19 ++++++++++--------- .../Services/ReCaptchaService.cs | 8 +++++++- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs index 803c0361494..85ff7df98ef 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs @@ -13,12 +13,15 @@ public static class ServiceCollectionExtensions { public static IServiceCollection AddReCaptcha(this IServiceCollection services, Action configure = null) { - services.AddHttpClient() - .AddTransientHttpErrorPolicy(policy => policy.WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(0.5 * attempt))); + services.AddHttpClient(ReCaptchaClient.Name, (sp, client) => + { + var settings = sp.GetRequiredService>().Value; + client.BaseAddress = new Uri(settings.ReCaptchaApiUri); + }).AddTransientHttpErrorPolicy(policy => policy.WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(0.5 * attempt))); services.AddTransient(); services.AddTransient, ReCaptchaSettingsConfiguration>(); - services.AddTransient(); + services.AddSingleton(); services.AddTagHelpers(); if (configure != null) diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs index 20836a58af3..759a6c4f0fe 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs @@ -1,24 +1,23 @@ -using System; using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using Newtonsoft.Json.Linq; -using OrchardCore.ReCaptcha.Configuration; namespace OrchardCore.ReCaptcha.Services { public class ReCaptchaClient { - private readonly HttpClient _httpClient; + public const string Name = "ReCaptcha"; + + private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; - public ReCaptchaClient(HttpClient httpClient, IOptions optionsAccessor, ILogger logger) + public ReCaptchaClient( + IHttpClientFactory httpClientFactory, + ILogger logger) { - var options = optionsAccessor.Value; - _httpClient = httpClient; - _httpClient.BaseAddress = new Uri(options.ReCaptchaApiUri); + _httpClientFactory = httpClientFactory; _logger = logger; } @@ -42,7 +41,9 @@ public async Task VerifyAsync(string responseToken, string secretKey) }); try { - var response = await _httpClient.PostAsync("siteverify", content); + var client = _httpClientFactory.CreateClient(Name); + + var response = await client.PostAsync("siteverify", content); response.EnsureSuccessStatusCode(); var responseJson = await response.Content.ReadAsStringAsync(); var responseModel = JObject.Parse(responseJson); diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs index dba626685b9..ca90d6314e5 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs @@ -21,7 +21,13 @@ public class ReCaptchaService private readonly ILogger _logger; protected readonly IStringLocalizer S; - public ReCaptchaService(ReCaptchaClient reCaptchaClient, IOptions optionsAccessor, IEnumerable robotDetectors, IHttpContextAccessor httpContextAccessor, ILogger logger, IStringLocalizer stringLocalizer) + public ReCaptchaService( + ReCaptchaClient reCaptchaClient, + IOptions optionsAccessor, + IEnumerable robotDetectors, + IHttpContextAccessor httpContextAccessor, + ILogger logger, + IStringLocalizer stringLocalizer) { _reCaptchaClient = reCaptchaClient; _settings = optionsAccessor.Value; From 2278c7b5e59f30191e5c7f21f4cadd1198373124 Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Thu, 14 Sep 2023 12:07:38 -0700 Subject: [PATCH 2/8] cleanup --- .../ServiceCollectionExtensions.cs | 9 +++----- .../Services/ReCaptchaClient.cs | 22 ++++++++++++------- .../Services/ReCaptchaService.cs | 3 ++- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs index 85ff7df98ef..b651067963b 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs @@ -13,15 +13,12 @@ public static class ServiceCollectionExtensions { public static IServiceCollection AddReCaptcha(this IServiceCollection services, Action configure = null) { - services.AddHttpClient(ReCaptchaClient.Name, (sp, client) => - { - var settings = sp.GetRequiredService>().Value; - client.BaseAddress = new Uri(settings.ReCaptchaApiUri); - }).AddTransientHttpErrorPolicy(policy => policy.WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(0.5 * attempt))); + services.AddHttpClient() + .AddTransientHttpErrorPolicy(policy => policy.WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(0.5 * attempt))); services.AddTransient(); services.AddTransient, ReCaptchaSettingsConfiguration>(); - services.AddSingleton(); + services.AddScoped(); services.AddTagHelpers(); if (configure != null) diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs index 759a6c4f0fe..dcc8fbda9e8 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs @@ -2,22 +2,25 @@ using System.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Newtonsoft.Json.Linq; +using OrchardCore.ReCaptcha.Configuration; namespace OrchardCore.ReCaptcha.Services { public class ReCaptchaClient { - public const string Name = "ReCaptcha"; - private readonly IHttpClientFactory _httpClientFactory; + private readonly ReCaptchaSettings _reCaptchaSettings; private readonly ILogger _logger; public ReCaptchaClient( IHttpClientFactory httpClientFactory, + IOptions reCaptchaSettings, ILogger logger) { _httpClientFactory = httpClientFactory; + _reCaptchaSettings = reCaptchaSettings.Value; _logger = logger; } @@ -25,25 +28,28 @@ public ReCaptchaClient( /// Verifies the supplied token with ReCaptcha Api. /// /// Token received from the ReCaptcha UI. - /// Key entered by user in the secrets. /// A boolean indicating if the token is valid. - public async Task VerifyAsync(string responseToken, string secretKey) + public async Task VerifyAsync(string responseToken) { if (string.IsNullOrWhiteSpace(responseToken)) { return false; } + if (!_reCaptchaSettings.IsValid() || string.IsNullOrEmpty(_reCaptchaSettings.ReCaptchaApiUri)) + { + return false; + } + var content = new FormUrlEncodedContent(new Dictionary { - { "secret", secretKey }, + { "secret", _reCaptchaSettings.SecretKey }, { "response", responseToken } }); try { - var client = _httpClientFactory.CreateClient(Name); - - var response = await client.PostAsync("siteverify", content); + var client = _httpClientFactory.CreateClient(); + var response = await client.PostAsync(_reCaptchaSettings.ReCaptchaApiUri.TrimEnd('/') + "/siteverify", content); response.EnsureSuccessStatusCode(); var responseJson = await response.Content.ReadAsStringAsync(); var responseModel = JObject.Parse(responseJson); diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs index ca90d6314e5..516fccfd28c 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs @@ -71,7 +71,8 @@ public void ThisIsAHuman() /// public async Task VerifyCaptchaResponseAsync(string reCaptchaResponse) { - return !string.IsNullOrWhiteSpace(reCaptchaResponse) && await _reCaptchaClient.VerifyAsync(reCaptchaResponse, _settings.SecretKey); + return !string.IsNullOrWhiteSpace(reCaptchaResponse) && + await _reCaptchaClient.VerifyAsync(reCaptchaResponse); } /// From 6650e513d0cd96bfed076ad5a8273704c92186f3 Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Thu, 14 Sep 2023 12:11:40 -0700 Subject: [PATCH 3/8] cleanup --- .../Configuration/ReCaptchaSettings.cs | 6 +++--- .../OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs | 7 +------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Configuration/ReCaptchaSettings.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Configuration/ReCaptchaSettings.cs index 3ffb729a6ab..712080ad9ad 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Configuration/ReCaptchaSettings.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Configuration/ReCaptchaSettings.cs @@ -1,5 +1,3 @@ -using System; - namespace OrchardCore.ReCaptcha.Configuration { public class ReCaptchaSettings @@ -19,7 +17,9 @@ public class ReCaptchaSettings public bool IsValid() { - return !string.IsNullOrWhiteSpace(SiteKey) && !string.IsNullOrWhiteSpace(SecretKey); + return !string.IsNullOrWhiteSpace(SiteKey) + && !string.IsNullOrWhiteSpace(SecretKey) + && !string.IsNullOrWhiteSpace(ReCaptchaApiUri); } } } diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs index dcc8fbda9e8..832eaf2d9db 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs @@ -31,12 +31,7 @@ public ReCaptchaClient( /// A boolean indicating if the token is valid. public async Task VerifyAsync(string responseToken) { - if (string.IsNullOrWhiteSpace(responseToken)) - { - return false; - } - - if (!_reCaptchaSettings.IsValid() || string.IsNullOrEmpty(_reCaptchaSettings.ReCaptchaApiUri)) + if (string.IsNullOrWhiteSpace(responseToken) || !_reCaptchaSettings.IsValid()) { return false; } From 4cc1dc4bd0de50033a880be08f07857e5dff5157 Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Thu, 14 Sep 2023 12:57:33 -0700 Subject: [PATCH 4/8] Remove ReCaptchaClient --- .../Configuration/ReCaptchaSettings.cs | 11 ++-- .../ServiceCollectionExtensions.cs | 7 ++- .../Services/ReCaptchaClient.cs | 62 ------------------- .../Services/ReCaptchaResponse.cs | 6 ++ .../Services/ReCaptchaService.cs | 57 ++++++++++++++--- 5 files changed, 66 insertions(+), 77 deletions(-) delete mode 100644 src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs create mode 100644 src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaResponse.cs diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Configuration/ReCaptchaSettings.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Configuration/ReCaptchaSettings.cs index 712080ad9ad..f113586ca1b 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Configuration/ReCaptchaSettings.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Configuration/ReCaptchaSettings.cs @@ -15,11 +15,12 @@ public class ReCaptchaSettings /// public int DetectionThreshold { get; set; } = 5; + private bool? _isValid; + public bool IsValid() - { - return !string.IsNullOrWhiteSpace(SiteKey) - && !string.IsNullOrWhiteSpace(SecretKey) - && !string.IsNullOrWhiteSpace(ReCaptchaApiUri); - } + => _isValid ??= !string.IsNullOrWhiteSpace(SiteKey) + && !string.IsNullOrWhiteSpace(SecretKey) + && !string.IsNullOrWhiteSpace(ReCaptchaApiUri); + } } diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs index b651067963b..843252c6e65 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs @@ -13,7 +13,12 @@ public static class ServiceCollectionExtensions { public static IServiceCollection AddReCaptcha(this IServiceCollection services, Action configure = null) { - services.AddHttpClient() + services.AddHttpClient(nameof(ReCaptchaService), (serviceProvider, client) => + { + var reCaptchaSettings = serviceProvider.GetRequiredService>().Value; + + client.BaseAddress = new Uri(reCaptchaSettings.ReCaptchaApiUri.TrimEnd('/') + '/'); + }) .AddTransientHttpErrorPolicy(policy => policy.WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(0.5 * attempt))); services.AddTransient(); diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs deleted file mode 100644 index 832eaf2d9db..00000000000 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaClient.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Newtonsoft.Json.Linq; -using OrchardCore.ReCaptcha.Configuration; - -namespace OrchardCore.ReCaptcha.Services -{ - public class ReCaptchaClient - { - private readonly IHttpClientFactory _httpClientFactory; - private readonly ReCaptchaSettings _reCaptchaSettings; - private readonly ILogger _logger; - - public ReCaptchaClient( - IHttpClientFactory httpClientFactory, - IOptions reCaptchaSettings, - ILogger logger) - { - _httpClientFactory = httpClientFactory; - _reCaptchaSettings = reCaptchaSettings.Value; - _logger = logger; - } - - /// - /// Verifies the supplied token with ReCaptcha Api. - /// - /// Token received from the ReCaptcha UI. - /// A boolean indicating if the token is valid. - public async Task VerifyAsync(string responseToken) - { - if (string.IsNullOrWhiteSpace(responseToken) || !_reCaptchaSettings.IsValid()) - { - return false; - } - - var content = new FormUrlEncodedContent(new Dictionary - { - { "secret", _reCaptchaSettings.SecretKey }, - { "response", responseToken } - }); - try - { - var client = _httpClientFactory.CreateClient(); - var response = await client.PostAsync(_reCaptchaSettings.ReCaptchaApiUri.TrimEnd('/') + "/siteverify", content); - response.EnsureSuccessStatusCode(); - var responseJson = await response.Content.ReadAsStringAsync(); - var responseModel = JObject.Parse(responseJson); - - return responseModel["success"].Value(); - } - catch (HttpRequestException e) - { - _logger.LogError(e, "Could not contact Google to verify captcha."); - } - - return false; - } - } -} diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaResponse.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaResponse.cs new file mode 100644 index 00000000000..699ec2cdcd0 --- /dev/null +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaResponse.cs @@ -0,0 +1,6 @@ +namespace OrchardCore.ReCaptcha.Services; + +public class ReCaptchaResponse +{ + public bool Success { get; set; } +} diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs index 516fccfd28c..9b233f2fe5e 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; +using System.Net.Http.Json; +using System.Text.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Localization; @@ -14,23 +17,28 @@ namespace OrchardCore.ReCaptcha.Services { public class ReCaptchaService { - private readonly ReCaptchaClient _reCaptchaClient; - private readonly ReCaptchaSettings _settings; + private static readonly JsonSerializerOptions _jsonSerializerOptions = new() + { + PropertyNamingPolicy = SnakeCaseNamingPolicy.Instance, + }; + + private readonly ReCaptchaSettings _reCaptchaSettings; + private readonly IHttpClientFactory _httpClientFactory; private readonly IEnumerable _robotDetectors; private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger _logger; protected readonly IStringLocalizer S; public ReCaptchaService( - ReCaptchaClient reCaptchaClient, + IHttpClientFactory httpClientFactory, IOptions optionsAccessor, IEnumerable robotDetectors, IHttpContextAccessor httpContextAccessor, ILogger logger, IStringLocalizer stringLocalizer) { - _reCaptchaClient = reCaptchaClient; - _settings = optionsAccessor.Value; + _reCaptchaSettings = optionsAccessor.Value; + _httpClientFactory = httpClientFactory; _robotDetectors = robotDetectors; _httpContextAccessor = httpContextAccessor; _logger = logger; @@ -71,8 +79,9 @@ public void ThisIsAHuman() /// public async Task VerifyCaptchaResponseAsync(string reCaptchaResponse) { - return !string.IsNullOrWhiteSpace(reCaptchaResponse) && - await _reCaptchaClient.VerifyAsync(reCaptchaResponse); + return !string.IsNullOrWhiteSpace(reCaptchaResponse) + && _reCaptchaSettings.IsValid() + && await VerifyAsync(reCaptchaResponse); } /// @@ -81,7 +90,7 @@ public async Task VerifyCaptchaResponseAsync(string reCaptchaResponse) /// Lambda for reporting errors. public async Task ValidateCaptchaAsync(Action reportError) { - if (!_settings.IsValid()) + if (!_reCaptchaSettings.IsValid()) { _logger.LogWarning("The ReCaptcha settings are not valid"); return false; @@ -96,7 +105,7 @@ public async Task ValidateCaptchaAsync(Action reportError) reCaptchaResponse = _httpContextAccessor.HttpContext.Request.Form[Constants.ReCaptchaServerResponseHeaderName].ToString(); } - var isValid = !string.IsNullOrEmpty(reCaptchaResponse) && await VerifyCaptchaResponseAsync(reCaptchaResponse); + var isValid = await VerifyCaptchaResponseAsync(reCaptchaResponse); if (!isValid) { @@ -105,5 +114,35 @@ public async Task ValidateCaptchaAsync(Action reportError) return isValid; } + + /// + /// Verifies the supplied token with ReCaptcha Api. + /// + /// Token received from the ReCaptcha UI. + /// A boolean indicating if the token is valid. + private async Task VerifyAsync(string responseToken) + { + try + { + var content = new FormUrlEncodedContent(new Dictionary + { + { "secret", _reCaptchaSettings.SecretKey }, + { "response", responseToken } + }); + + var client = _httpClientFactory.CreateClient(nameof(ReCaptchaService)); + var response = await client.PostAsync("siteverify", content); + response.EnsureSuccessStatusCode(); + var result = await response.Content.ReadFromJsonAsync(_jsonSerializerOptions); + + return result.Success; + } + catch (HttpRequestException e) + { + _logger.LogError(e, "Could not contact Google to verify captcha."); + } + + return false; + } } } From 27b696bb40a11daf07a876a04f148194ff323b5a Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Thu, 14 Sep 2023 13:16:55 -0700 Subject: [PATCH 5/8] cleanup driver --- .../Forms/ReCaptchaPartDisplayDriver.cs | 12 +++++------- .../Configuration/ReCaptchaSettings.cs | 1 - 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPartDisplayDriver.cs index 7b203b6e67e..aa1a5ef6534 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPartDisplayDriver.cs @@ -1,5 +1,3 @@ -using System; -using System.Threading.Tasks; using OrchardCore.ContentManagement.Display.ContentDisplay; using OrchardCore.ContentManagement.Display.Models; using OrchardCore.DisplayManagement.Views; @@ -20,21 +18,21 @@ public ReCaptchaPartDisplayDriver(ISiteService siteService) public override IDisplayResult Display(ReCaptchaPart part, BuildPartDisplayContext context) { - return Initialize("ReCaptchaPart", (Func)(async m => + return Initialize("ReCaptchaPart", async model => { var siteSettings = await _siteService.GetSiteSettingsAsync(); var settings = siteSettings.As(); - m.SettingsAreConfigured = settings.IsValid(); - })).Location("Detail", "Content"); + model.SettingsAreConfigured = settings.IsValid(); + }).Location("Detail", "Content"); } public override IDisplayResult Edit(ReCaptchaPart part, BuildPartEditorContext context) { - return Initialize("ReCaptchaPart_Fields_Edit", async m => + return Initialize("ReCaptchaPart_Fields_Edit", async model => { var siteSettings = await _siteService.GetSiteSettingsAsync(); var settings = siteSettings.As(); - m.SettingsAreConfigured = settings.IsValid(); + model.SettingsAreConfigured = settings.IsValid(); }); } } diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Configuration/ReCaptchaSettings.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Configuration/ReCaptchaSettings.cs index f113586ca1b..9ef25e413fb 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Configuration/ReCaptchaSettings.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Configuration/ReCaptchaSettings.cs @@ -21,6 +21,5 @@ public bool IsValid() => _isValid ??= !string.IsNullOrWhiteSpace(SiteKey) && !string.IsNullOrWhiteSpace(SecretKey) && !string.IsNullOrWhiteSpace(ReCaptchaApiUri); - } } From 8142c91e8ef00f6a7544d5b2360ff2e7875ed4dd Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Mon, 25 Sep 2023 16:01:30 -0700 Subject: [PATCH 6/8] cleanup --- src/OrchardCore.Modules/OrchardCore.ReCaptcha/Startup.cs | 2 +- .../ServiceCollectionExtensions.cs | 9 ++------- .../Services/ReCaptchaService.cs | 9 ++++----- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Startup.cs index 68c7c569b9d..0e55ae780bd 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Startup.cs @@ -47,7 +47,7 @@ public override void ConfigureServices(IServiceCollection services) services.AddScoped(); services.Configure((options) => { - options.Filters.Add(typeof(ReCaptchaLoginFilter)); + options.Filters.Add(); }); } } diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs index 843252c6e65..861f52dca49 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs @@ -13,17 +13,12 @@ public static class ServiceCollectionExtensions { public static IServiceCollection AddReCaptcha(this IServiceCollection services, Action configure = null) { - services.AddHttpClient(nameof(ReCaptchaService), (serviceProvider, client) => - { - var reCaptchaSettings = serviceProvider.GetRequiredService>().Value; - - client.BaseAddress = new Uri(reCaptchaSettings.ReCaptchaApiUri.TrimEnd('/') + '/'); - }) + services.AddScoped() + .AddHttpClient() .AddTransientHttpErrorPolicy(policy => policy.WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(0.5 * attempt))); services.AddTransient(); services.AddTransient, ReCaptchaSettingsConfiguration>(); - services.AddScoped(); services.AddTagHelpers(); if (configure != null) diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs index 9b233f2fe5e..fc800dbb5b0 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs @@ -23,14 +23,14 @@ public class ReCaptchaService }; private readonly ReCaptchaSettings _reCaptchaSettings; - private readonly IHttpClientFactory _httpClientFactory; + private readonly HttpClient _httpClient; private readonly IEnumerable _robotDetectors; private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger _logger; protected readonly IStringLocalizer S; public ReCaptchaService( - IHttpClientFactory httpClientFactory, + HttpClient httpClient, IOptions optionsAccessor, IEnumerable robotDetectors, IHttpContextAccessor httpContextAccessor, @@ -38,7 +38,7 @@ public ReCaptchaService( IStringLocalizer stringLocalizer) { _reCaptchaSettings = optionsAccessor.Value; - _httpClientFactory = httpClientFactory; + _httpClient = httpClient; _robotDetectors = robotDetectors; _httpContextAccessor = httpContextAccessor; _logger = logger; @@ -130,8 +130,7 @@ private async Task VerifyAsync(string responseToken) { "response", responseToken } }); - var client = _httpClientFactory.CreateClient(nameof(ReCaptchaService)); - var response = await client.PostAsync("siteverify", content); + var response = await _httpClient.PostAsync($"{_reCaptchaSettings.ReCaptchaApiUri.TrimEnd('/')}/siteverify", content); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(_jsonSerializerOptions); From e419a6006ebae615c0374299c4b5beec2647bb66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Ros?= Date: Thu, 19 Oct 2023 11:04:59 -0700 Subject: [PATCH 7/8] Update ServiceCollectionExtensions.cs --- .../OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs index 41138e39135..9b750f5cc63 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/ServiceCollectionExtensions.cs @@ -13,6 +13,7 @@ public static class ServiceCollectionExtensions { public static IServiceCollection AddReCaptcha(this IServiceCollection services, Action configure = null) { + // c.f. https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests services.AddScoped() .AddHttpClient() .AddTransientHttpErrorPolicy(policy => policy.WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(0.5 * attempt))); From 23d243bdbc3b9605c333109a66724910eb16eb3b Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Thu, 19 Oct 2023 11:42:10 -0700 Subject: [PATCH 8/8] Fix build and cleanup --- .../Services/ReCaptchaService.cs | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs index cd3c85fd115..d73d10c5a07 100644 --- a/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs +++ b/src/OrchardCore/OrchardCore.ReCaptcha.Core/Services/ReCaptchaService.cs @@ -6,7 +6,6 @@ using System.Text.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -25,8 +24,6 @@ public class ReCaptchaService private readonly ReCaptchaSettings _reCaptchaSettings; private readonly HttpClient _httpClient; - private readonly ReCaptchaSettings _settings; - private readonly IEnumerable _robotDetectors; private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger _logger; @@ -52,40 +49,33 @@ public ReCaptchaService( /// Flags the behavior as that of a robot. /// public void MaybeThisIsARobot() - { - _robotDetectors.Invoke(i => i.FlagAsRobot(), _logger); - } + => _robotDetectors.Invoke(i => i.FlagAsRobot(), _logger); /// /// Determines if the request has been made by a robot. /// /// Yes (true) or no (false). public bool IsThisARobot() - { - var result = _robotDetectors.Invoke(i => i.DetectRobot(), _logger); - return result.Any(a => a.IsRobot); - } + => _robotDetectors.Invoke(i => i.DetectRobot(), _logger) + .Any(a => a.IsRobot); /// /// Clears all robot markers, we are dealing with a human. /// /// public void ThisIsAHuman() - { - _robotDetectors.Invoke(i => i.IsNotARobot(), _logger); - } + => _robotDetectors.Invoke(i => i.IsNotARobot(), _logger); /// /// Verifies the ReCaptcha response with the ReCaptcha webservice. /// /// /// - public Task VerifyCaptchaResponseAsync(string reCaptchaResponse) - { - return !string.IsNullOrWhiteSpace(reCaptchaResponse) + public async Task VerifyCaptchaResponseAsync(string reCaptchaResponse) + => !string.IsNullOrWhiteSpace(reCaptchaResponse) && _reCaptchaSettings.IsValid() && await VerifyAsync(reCaptchaResponse); - } + /// /// Validates the captcha that is in the Form of the current request. @@ -95,7 +85,7 @@ public async Task ValidateCaptchaAsync(Action reportError) { if (!_reCaptchaSettings.IsValid()) { - _logger.LogWarning("The ReCaptcha settings are not valid"); + _logger.LogWarning("The ReCaptcha settings are invalid"); return false; } @@ -112,7 +102,7 @@ public async Task ValidateCaptchaAsync(Action reportError) if (!isValid) { - reportError("ReCaptcha", S["Failed to validate captcha"]); + reportError("ReCaptcha", S["Failed to validate ReCaptcha"]); } return isValid; @@ -141,7 +131,7 @@ private async Task VerifyAsync(string responseToken) } catch (HttpRequestException e) { - _logger.LogError(e, "Could not contact Google to verify captcha."); + _logger.LogError(e, "Could not contact Google to verify ReCaptcha."); } return false;