diff --git a/src/OrchardCore.Modules/OrchardCore.Users/Controllers/AccountController.cs b/src/OrchardCore.Modules/OrchardCore.Users/Controllers/AccountController.cs index 6db514be774..a264bde7205 100644 --- a/src/OrchardCore.Modules/OrchardCore.Users/Controllers/AccountController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Users/Controllers/AccountController.cs @@ -13,7 +13,6 @@ using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using OrchardCore.DisplayManagement.Notify; -using OrchardCore.Entities; using OrchardCore.Modules; using OrchardCore.Mvc.Core.Utilities; using OrchardCore.Settings; diff --git a/src/OrchardCore.Modules/OrchardCore.Users/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Users/Startup.cs index f538777369c..21ec8b3c407 100644 --- a/src/OrchardCore.Modules/OrchardCore.Users/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Users/Startup.cs @@ -17,8 +17,8 @@ using OrchardCore.Admin.Models; using OrchardCore.Data; using OrchardCore.Data.Migration; -using OrchardCore.DisplayManagement.Descriptors; using OrchardCore.Deployment; +using OrchardCore.DisplayManagement.Descriptors; using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Theming; using OrchardCore.Environment.Commands; diff --git a/src/OrchardCore/OrchardCore.Mvc.Core/ModelBinding/SafeBoolModelBinder.cs b/src/OrchardCore/OrchardCore.Mvc.Core/ModelBinding/SafeBoolModelBinder.cs new file mode 100644 index 00000000000..e5a54dfdb24 --- /dev/null +++ b/src/OrchardCore/OrchardCore.Mvc.Core/ModelBinding/SafeBoolModelBinder.cs @@ -0,0 +1,40 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; + +namespace OrchardCore.Mvc.ModelBinding; + +/// +/// Model binder to produce a validation error when a Boolean field contains a value that's not a valid bool. The +/// default model binder would throw a . That's an issue for e.g. the Users module, see +/// . +/// +internal sealed class SafeBoolModelBinder : IModelBinder +{ + public Task BindModelAsync(ModelBindingContext bindingContext) + { + ArgumentNullException.ThrowIfNull(nameof(bindingContext)); + + var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); + + if (valueProviderResult == ValueProviderResult.None) + { + return Task.CompletedTask; + } + + if (bool.TryParse(valueProviderResult.FirstValue, out bool result)) + { + bindingContext.Result = ModelBindingResult.Success(result); + + return Task.CompletedTask; + } + + var localizer = bindingContext.HttpContext.RequestServices.GetService>(); + + bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, localizer["Invalid Boolean value."]); + + return Task.CompletedTask; + } +} diff --git a/src/OrchardCore/OrchardCore.Mvc.Core/ModelBinding/SafeBoolModelBinderProvider.cs b/src/OrchardCore/OrchardCore.Mvc.Core/ModelBinding/SafeBoolModelBinderProvider.cs new file mode 100644 index 00000000000..46aecd0447e --- /dev/null +++ b/src/OrchardCore/OrchardCore.Mvc.Core/ModelBinding/SafeBoolModelBinderProvider.cs @@ -0,0 +1,19 @@ +using System; +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace OrchardCore.Mvc.ModelBinding; + +internal sealed class SafeBoolModelBinderProvider : IModelBinderProvider +{ + public IModelBinder GetBinder(ModelBinderProviderContext context) + { + ArgumentNullException.ThrowIfNull(context, nameof(context)); + + if (context.Metadata.ModelType == typeof(bool)) + { + return new SafeBoolModelBinder(); + } + + return null; + } +} diff --git a/src/OrchardCore/OrchardCore.Mvc.Core/Startup.cs b/src/OrchardCore/OrchardCore.Mvc.Core/Startup.cs index 9d11747fa13..8b19b315ffb 100644 --- a/src/OrchardCore/OrchardCore.Mvc.Core/Startup.cs +++ b/src/OrchardCore/OrchardCore.Mvc.Core/Startup.cs @@ -82,6 +82,8 @@ public override void ConfigureServices(IServiceCollection services) // Custom model binder to testing purpose options.ModelBinderProviders.Insert(0, new CheckMarkModelBinderProvider()); + + options.ModelBinderProviders.Insert(0, new SafeBoolModelBinderProvider()); }); // Add a route endpoint selector policy.