Added configuration validation for content and request handler settings.

This commit is contained in:
Andy Butland
2020-09-18 11:30:26 +02:00
parent d7ab7d3d2e
commit 1f8d7f18be
11 changed files with 136 additions and 15 deletions

View File

@@ -4,12 +4,25 @@ namespace Umbraco.Core.Configuration.Models
{
public class ContentErrorPage
{
//TODO introduce validation, to check only one of key/id/xPath is used.
public int ContentId { get; }
public Guid ContentKey { get; }
public string ContentXPath { get; }
public bool HasContentId { get; }
public bool HasContentKey { get; }
public int ContentId { get; set; }
public Guid ContentKey { get; set; }
public string ContentXPath { get; set; }
public bool HasContentId => ContentId != 0;
public bool HasContentKey => ContentKey != Guid.Empty;
public bool HasContentXPath => !string.IsNullOrEmpty(ContentXPath);
public string Culture { get; set; }
public bool IsValid()
{
// Entry is valid if Culture and one and only one of ContentId, ContentKey or ContentXPath is provided.
return !string.IsNullOrWhiteSpace(Culture) &&
((HasContentId ? 1 : 0) + (HasContentKey ? 1 : 0) + (HasContentXPath ? 1 : 0) == 1);
}
}
}

View File

@@ -24,9 +24,13 @@ namespace Umbraco.Core.Configuration.Models
private class ImagingAutoFillUploadField : IImagingAutoFillUploadField
{
public string Alias { get; set; }
public string WidthFieldAlias { get; set; }
public string HeightFieldAlias { get; set; }
public string LengthFieldAlias { get; set; }
public string ExtensionFieldAlias { get; set; }
}
}

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Macros;
namespace Umbraco.Core.Configuration.Models
@@ -16,11 +15,21 @@ namespace Umbraco.Core.Configuration.Models
public bool ResolveUrlsFromTextString { get; set; } = false;
public IEnumerable<ContentErrorPage> Error404Collection { get; set; } = Array.Empty<ContentErrorPage>();
public ContentErrorPage[] Error404Collection { get; set; } = Array.Empty<ContentErrorPage>();
public string PreviewBadge { get; set; } = DefaultPreviewBadge;
public MacroErrorBehaviour MacroErrors { get; set; } = MacroErrorBehaviour.Inline;
public string MacroErrors { get; set; } = MacroErrorBehaviour.Inline.ToString();
public MacroErrorBehaviour MacroErrorsBehaviour
{
get
{
return Enum.TryParse<MacroErrorBehaviour>(MacroErrors, true, out var value)
? value
: MacroErrorBehaviour.Inline;
}
}
public IEnumerable<string> DisallowedUploadFiles { get; set; } = new[] { "ashx", "aspx", "ascx", "config", "cshtml", "vbhtml", "asmx", "air", "axd" };

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Core.Configuration.Models.Validation
{
public abstract class ConfigurationValidationBase
{
public bool ValidateStringIsOneOfValidValues(string configPath, string value, IEnumerable<string> validValues, out string message)
{
if (!validValues.InvariantContains(value))
{
message = $"Configuration entry {configPath} contains an invalid value '{value}', it should be one of the following: '{string.Join(", ", validValues)}'.";
return false;
}
message = string.Empty;
return true;
}
public bool ValidateStringIsOneOfEnumValues(string configPath, string value, Type enumType, out string message)
{
var validValues = Enum.GetValues(enumType).OfType<object>().Select(x => x.ToString().ToFirstLowerInvariant());
return ValidateStringIsOneOfValidValues(configPath, value, validValues, out message);
}
}
}

View File

@@ -0,0 +1,43 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Options;
using Umbraco.Core.Macros;
namespace Umbraco.Core.Configuration.Models.Validation
{
public class ContentSettingsValidation : ConfigurationValidationBase, IValidateOptions<ContentSettings>
{
public ValidateOptionsResult Validate(string name, ContentSettings options)
{
string message;
if (!ValidateMacroErrors(options.MacroErrors, out message))
{
return ValidateOptionsResult.Fail(message);
}
if (!ValidateError404Collection(options.Error404Collection, out message))
{
return ValidateOptionsResult.Fail(message);
}
return ValidateOptionsResult.Success;
}
private bool ValidateMacroErrors(string value, out string message)
{
return ValidateStringIsOneOfEnumValues("Content:MacroErrors", value, typeof(MacroErrorBehaviour), out message);
}
private bool ValidateError404Collection(IEnumerable<ContentErrorPage> values, out string message)
{
if (values.Any(x => !x.IsValid()))
{
message = $"Configuration entry Content:Error404Collection contains one or more invalid values. Culture and one and only one of ContentId, ContentKey and ContentXPath must be specified for each entry.";
return false;
}
message = string.Empty;
return true;
}
}
}

View File

@@ -0,0 +1,23 @@
using Microsoft.Extensions.Options;
namespace Umbraco.Core.Configuration.Models.Validation
{
public class RequestHandlerSettingsValidation : ConfigurationValidationBase, IValidateOptions<RequestHandlerSettings>
{
public ValidateOptionsResult Validate(string name, RequestHandlerSettings options)
{
if (!ValidateConvertUrlsToAscii(options.ConvertUrlsToAscii, out var message))
{
return ValidateOptionsResult.Fail(message);
}
return ValidateOptionsResult.Success;
}
private bool ValidateConvertUrlsToAscii(string value, out string message)
{
var validValues = new[] { "try", "true", "false" };
return ValidateStringIsOneOfValidValues("RequestHandler:ConvertUrlsToAscii", value, validValues, out message);
}
}
}

View File

@@ -62,7 +62,7 @@ namespace Umbraco.Web.Routing
}
/// <summary>
/// Returns the content id based on the configured IContentErrorPage section
/// Returns the content id based on the configured ContentErrorPage section.
/// </summary>
/// <param name="errorPage"></param>
/// <param name="entityService"></param>

View File

@@ -1,5 +1,4 @@
using Moq;
using NUnit.Framework;
using NUnit.Framework;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Models;
using Umbraco.Tests.Common.Builders;

View File

@@ -18,6 +18,7 @@ using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Configuration.Models.Validation;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Logging.Serilog;
@@ -126,6 +127,9 @@ namespace Umbraco.Extensions
{
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
services.AddSingleton<IValidateOptions<ContentSettings>, ContentSettingsValidation>();
services.AddSingleton<IValidateOptions<RequestHandlerSettings>, RequestHandlerSettingsValidation>();
services.Configure<ActiveDirectorySettings>(configuration.GetSection(Constants.Configuration.ConfigPrefix + "ActiveDirectory"));
services.Configure<ConnectionStrings>(configuration.GetSection("ConnectionStrings"), o => o.BindNonPublicProperties = true);
services.Configure<ContentSettings>(configuration.GetSection(Constants.Configuration.ConfigPrefix + "Content"));
@@ -149,7 +153,6 @@ namespace Umbraco.Extensions
services.Configure<UserPasswordConfigurationSettings>(configuration.GetSection(Constants.Configuration.ConfigSecurityPrefix + "UserPassword"));
services.Configure<WebRoutingSettings>(configuration.GetSection(Constants.Configuration.ConfigPrefix + "WebRouting"));
return services;
}

View File

@@ -290,7 +290,7 @@ namespace Umbraco.Web.Macros
Alias = macro.Alias,
MacroSource = macro.MacroSource,
Exception = e,
Behaviour = _contentSettings.MacroErrors
Behaviour = _contentSettings.MacroErrorsBehaviour
};
switch (macroErrorEventArgs.Behaviour)

View File

@@ -290,7 +290,7 @@ namespace Umbraco.Web.Macros
Alias = macro.Alias,
MacroSource = macro.MacroSource,
Exception = e,
Behaviour = _contentSettings.MacroErrors
Behaviour = _contentSettings.MacroErrorsBehaviour
};
switch (macroErrorEventArgs.Behaviour)