Merge branch 'v8/8.1' into v8/dev

# Conflicts:
#	src/SolutionInfo.cs
#	src/Umbraco.Web/HtmlHelperRenderExtensions.cs
#	src/Umbraco.Web/Mvc/HttpUmbracoFormRouteStringException.cs
#	src/Umbraco.Web/Mvc/ValidateUmbracoFormRouteStringAttribute.cs
This commit is contained in:
Shannon
2019-07-17 22:31:54 +10:00
6 changed files with 60 additions and 15 deletions

View File

@@ -267,6 +267,7 @@
<Compile Include="StringNewlineExtensions.cs" />
<Compile Include="Strings\StylesheetHelperTests.cs" />
<Compile Include="Strings\StringValidationTests.cs" />
<Compile Include="Web\Mvc\ValidateUmbracoFormRouteStringAttributeTests.cs" />
<Compile Include="Web\UmbracoHelperTests.cs" />
<Compile Include="Membership\MembershipProviderBaseTests.cs" />
<Compile Include="Membership\UmbracoServiceMembershipProviderTests.cs" />

View File

@@ -4,6 +4,7 @@ using Umbraco.Web;
namespace Umbraco.Tests.Web.Mvc
{
[TestFixture]
public class HtmlHelperExtensionMethodsTests
{

View File

@@ -0,0 +1,37 @@
using NUnit.Framework;
using Umbraco.Web;
using Umbraco.Web.Mvc;
namespace Umbraco.Tests.Web.Mvc
{
[TestFixture]
public class ValidateUmbracoFormRouteStringAttributeTests
{
[Test]
public void Validate_Route_String()
{
var attribute = new ValidateUmbracoFormRouteStringAttribute();
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(null, null, null, null));
const string ControllerName = "Test";
const string ControllerAction = "Index";
const string Area = "MyArea";
var validUfprt = UrlHelperRenderExtensions.CreateEncryptedRouteString(ControllerName, ControllerAction, Area);
var invalidUfprt = validUfprt + "z";
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(invalidUfprt, null, null, null));
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(validUfprt, ControllerName, ControllerAction, "doesntMatch"));
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(validUfprt, ControllerName, ControllerAction, null));
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(validUfprt, ControllerName, "doesntMatch", Area));
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(validUfprt, ControllerName, null, Area));
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(validUfprt, "doesntMatch", ControllerAction, Area));
Assert.Throws<HttpUmbracoFormRouteStringException>(() => attribute.ValidateRouteString(validUfprt, null, ControllerAction, Area));
Assert.DoesNotThrow(() => attribute.ValidateRouteString(validUfprt, ControllerName, ControllerAction, Area));
Assert.DoesNotThrow(() => attribute.ValidateRouteString(validUfprt, ControllerName.ToLowerInvariant(), ControllerAction.ToLowerInvariant(), Area.ToLowerInvariant()));
}
}
}

View File

@@ -1,23 +1,17 @@
using System;
using System.Net;
using System.Runtime.Serialization;
using System.Web;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Describes an exception that occurred during the processing of an Umbraco form route string.
/// Exception that occurs when an Umbraco form route string is invalid
/// </summary>
/// <seealso cref="System.Web.HttpException" />
[Serializable]
public sealed class HttpUmbracoFormRouteStringException : HttpException
{
/// <summary>
/// Initializes a new instance of the <see cref="HttpUmbracoFormRouteStringException" /> class.
/// </summary>
public HttpUmbracoFormRouteStringException()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="HttpUmbracoFormRouteStringException" /> class.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
@@ -42,5 +36,6 @@ namespace Umbraco.Web.Mvc
public HttpUmbracoFormRouteStringException(string message, Exception innerException)
: base(message, innerException)
{ }
}
}

View File

@@ -1,14 +1,21 @@
using System;
using System.Net;
using System.Net.Http;
using System.Web.Mvc;
using Umbraco.Core;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Represents an attribute that is used to prevent an invalid Umbraco form request route string on a request.
/// Attribute used to check that the request contains a valid Umbraco form request string.
/// </summary>
/// <seealso cref="System.Web.Mvc.FilterAttribute" />
/// <seealso cref="System.Web.Mvc.IAuthorizationFilter" />
/// <remarks>
/// Applying this attribute/filter to a <see cref="SurfaceController"/> or SurfaceController Action will ensure that the Action can only be executed
/// when it is routed to from within Umbraco, typically when rendering a form with BegingUmbracoForm. It will mean that the natural MVC route for this Action
/// will fail with a <see cref="HttpUmbracoFormRouteStringException"/>.
/// </remarks>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class ValidateUmbracoFormRouteStringAttribute : FilterAttribute, IAuthorizationFilter
{
@@ -25,11 +32,14 @@ namespace Umbraco.Web.Mvc
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException(nameof(filterContext));
}
var ufprt = filterContext.HttpContext.Request["ufprt"];
ValidateRouteString(ufprt, filterContext.ActionDescriptor?.ControllerDescriptor.ControllerName, filterContext.ActionDescriptor?.ActionName, filterContext.RouteData?.DataTokens["area"]?.ToString());
}
public void ValidateRouteString(string ufprt, string currentController, string currentAction, string currentArea)
{
if (ufprt.IsNullOrWhiteSpace())
{
throw new HttpUmbracoFormRouteStringException("The required request field \"ufprt\" is not present.");
@@ -40,12 +50,13 @@ namespace Umbraco.Web.Mvc
throw new HttpUmbracoFormRouteStringException("The Umbraco form request route string could not be decrypted.");
}
if (additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Controller] != filterContext.ActionDescriptor.ControllerDescriptor.ControllerName ||
additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Action] != filterContext.ActionDescriptor.ActionName ||
additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Area].NullOrWhiteSpaceAsNull() != filterContext.RouteData.DataTokens["area"]?.ToString().NullOrWhiteSpaceAsNull())
if (!additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Controller].InvariantEquals(currentController) ||
!additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Action].InvariantEquals(currentAction) ||
(!additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Area].IsNullOrWhiteSpace() && !additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Area].InvariantEquals(currentArea)))
{
throw new HttpUmbracoFormRouteStringException("The provided Umbraco form request route string was meant for a different controller and action.");
}
}
}
}

View File

@@ -820,7 +820,7 @@ namespace Umbraco.Web
{
decryptedString = ufprt.DecryptWithMachineKey();
}
catch (FormatException)
catch (Exception ex) when (ex is FormatException || ex is ArgumentException)
{
Current.Logger.Warn(typeof(UmbracoHelper), "A value was detected in the ufprt parameter but Umbraco could not decrypt the string");
parts = null;