diff --git a/src/Umbraco.Core/Configuration/Models/WebRoutingSettings.cs b/src/Umbraco.Core/Configuration/Models/WebRoutingSettings.cs
index 9f06046452..d94fdd8496 100644
--- a/src/Umbraco.Core/Configuration/Models/WebRoutingSettings.cs
+++ b/src/Umbraco.Core/Configuration/Models/WebRoutingSettings.cs
@@ -10,6 +10,17 @@ namespace Umbraco.Core.Configuration.Models
///
public class WebRoutingSettings
{
+ ///
+ /// Gets or sets a value indicating whether to check if any routed endpoints match a front-end request before
+ /// the Umbraco dynamic router tries to map the request to an Umbraco content item.
+ ///
+ ///
+ /// This should not be necessary if the Umbraco catch-all/dynamic route is registered last like it's supposed to be. In that case
+ /// ASP.NET Core will automatically handle this in all cases. This is more of a backward compatible option since this is what v7/v8 used
+ /// to do.
+ ///
+ public bool TryMatchingEndpointsForAllPages { get; set; } = false;
+
///
/// Gets or sets a value indicating whether IIS custom errors should be skipped.
///
diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs
index 96f01d111a..260ec5487f 100644
--- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs
+++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs
@@ -24,6 +24,7 @@ using Umbraco.Core.Mail;
using Umbraco.Core.Manifest;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
+using Umbraco.Core.Routing;
using Umbraco.Core.Runtime;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
@@ -146,6 +147,7 @@ namespace Umbraco.Core.DependencyInjection
this.AddNotificationHandler();
Services.AddSingleton();
+ Services.AddSingleton();
this.AddNotificationHandler();
Services.AddUnique();
diff --git a/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs b/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs
new file mode 100644
index 0000000000..8e8541cb2c
--- /dev/null
+++ b/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Microsoft.Extensions.Options;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Configuration.Models;
+using Umbraco.Core.Hosting;
+
+namespace Umbraco.Core.Routing
+{
+ ///
+ /// Utility for checking paths
+ ///
+ public class UmbracoRequestPaths
+ {
+ private readonly string _backOfficePath;
+ private readonly string _mvcArea;
+ private readonly string _backOfficeMvcPath;
+ private readonly string _previewMvcPath;
+ private readonly string _surfaceMvcPath;
+ private readonly string _apiMvcPath;
+ private readonly string _installPath;
+ private readonly string _appPath;
+ private readonly List _defaultUmbPaths;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public UmbracoRequestPaths(IOptions globalSettings, IHostingEnvironment hostingEnvironment)
+ {
+ var applicationPath = hostingEnvironment.ApplicationVirtualPath;
+ _appPath = applicationPath.TrimStart('/');
+
+ _backOfficePath = globalSettings.Value.GetBackOfficePath(hostingEnvironment)
+ .EnsureStartsWith('/').TrimStart(_appPath.EnsureStartsWith('/')).EnsureStartsWith('/');
+
+ _mvcArea = globalSettings.Value.GetUmbracoMvcArea(hostingEnvironment);
+ _defaultUmbPaths = new List { "/" + _mvcArea, "/" + _mvcArea + "/" };
+ _backOfficeMvcPath = "/" + _mvcArea + "/BackOffice/";
+ _previewMvcPath = "/" + _mvcArea + "/Preview/";
+ _surfaceMvcPath = "/" + _mvcArea + "/Surface/";
+ _apiMvcPath = "/" + _mvcArea + "/Api/";
+ _installPath = hostingEnvironment.ToAbsolute(Constants.SystemDirectories.Install);
+ }
+
+ ///
+ /// Checks if the current uri is a back office request
+ ///
+ ///
+ ///
+ /// There are some special routes we need to check to properly determine this:
+ ///
+ ///
+ /// These are def back office:
+ /// /Umbraco/BackOffice = back office
+ /// /Umbraco/Preview = back office
+ ///
+ ///
+ /// If it's not any of the above then we cannot determine if it's back office or front-end
+ /// so we can only assume that it is not back office. This will occur if people use an UmbracoApiController for the backoffice
+ /// but do not inherit from UmbracoAuthorizedApiController and do not use [IsBackOffice] attribute.
+ ///
+ ///
+ /// These are def front-end:
+ /// /Umbraco/Surface = front-end
+ /// /Umbraco/Api = front-end
+ /// But if we've got this far we'll just have to assume it's front-end anyways.
+ ///
+ ///
+ public bool IsBackOfficeRequest(string absPath)
+ {
+ var fullUrlPath = absPath.TrimStart('/');
+ var urlPath = fullUrlPath.TrimStart(_appPath).EnsureStartsWith('/');
+
+ // check if this is in the umbraco back office
+ var isUmbracoPath = urlPath.InvariantStartsWith(_backOfficePath);
+
+ // if not, then def not back office
+ if (isUmbracoPath == false)
+ {
+ return false;
+ }
+
+ // if its the normal /umbraco path
+ if (_defaultUmbPaths.Any(x => urlPath.InvariantEquals(x)))
+ {
+ return true;
+ }
+
+ // check for special back office paths
+ if (urlPath.InvariantStartsWith(_backOfficeMvcPath)
+ || urlPath.InvariantStartsWith(_previewMvcPath))
+ {
+ return true;
+ }
+
+ // check for special front-end paths
+ if (urlPath.InvariantStartsWith(_surfaceMvcPath)
+ || urlPath.InvariantStartsWith(_apiMvcPath))
+ {
+ return false;
+ }
+
+ // if its none of the above, we will have to try to detect if it's a PluginController route, we can detect this by
+ // checking how many parts the route has, for example, all PluginController routes will be routed like
+ // Umbraco/MYPLUGINAREA/MYCONTROLLERNAME/{action}/{id}
+ // so if the path contains at a minimum 3 parts: Umbraco + MYPLUGINAREA + MYCONTROLLERNAME then we will have to assume it is a
+ // plugin controller for the front-end.
+ if (urlPath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Length >= 3)
+ {
+ return false;
+ }
+
+ // if its anything else we can assume it's back office
+ return true;
+ }
+
+ ///
+ /// Checks if the current uri is an install request
+ ///
+ public bool IsInstallerRequest(string absPath) => absPath.InvariantStartsWith(_installPath);
+
+ ///
+ /// Rudimentary check to see if it's not a server side request
+ ///
+ public bool IsClientSideRequest(string absPath)
+ {
+ var ext = Path.GetExtension(absPath);
+ return !ext.IsNullOrWhiteSpace();
+ }
+ }
+}
diff --git a/src/Umbraco.Core/UriExtensions.cs b/src/Umbraco.Core/UriExtensions.cs
index ea846f7f7a..497159309a 100644
--- a/src/Umbraco.Core/UriExtensions.cs
+++ b/src/Umbraco.Core/UriExtensions.cs
@@ -13,151 +13,6 @@ namespace Umbraco.Core
///
public static class UriExtensions
{
- ///
- /// Checks if the current uri is a back office request
- ///
- ///
- ///
- ///
- ///
- ///
- /// There are some special routes we need to check to properly determine this:
- ///
- /// If any route has an extension in the path like .aspx = back office
- ///
- /// These are def back office:
- /// /Umbraco/BackOffice = back office
- /// /Umbraco/Preview = back office
- /// If it's not any of the above, and there's no extension then we cannot determine if it's back office or front-end
- /// so we can only assume that it is not back office. This will occur if people use an UmbracoApiController for the backoffice
- /// but do not inherit from UmbracoAuthorizedApiController and do not use [IsBackOffice] attribute.
- ///
- /// These are def front-end:
- /// /Umbraco/Surface = front-end
- /// /Umbraco/Api = front-end
- /// But if we've got this far we'll just have to assume it's front-end anyways.
- ///
- ///
- public static bool IsBackOfficeRequest(this Uri url, GlobalSettings globalSettings, IHostingEnvironment hostingEnvironment)
- {
- var applicationPath = hostingEnvironment.ApplicationVirtualPath;
-
- var fullUrlPath = url.AbsolutePath.TrimStart(new[] {'/'});
- var appPath = applicationPath.TrimStart(new[] {'/'});
- var urlPath = fullUrlPath.TrimStart(appPath).EnsureStartsWith('/');
-
- //check if this is in the umbraco back office
- var backOfficePath = globalSettings.GetBackOfficePath(hostingEnvironment);
- var isUmbracoPath = urlPath.InvariantStartsWith(backOfficePath.EnsureStartsWith('/').TrimStart(appPath.EnsureStartsWith('/')).EnsureStartsWith('/'));
- //if not, then def not back office
- if (isUmbracoPath == false) return false;
-
- var mvcArea = globalSettings.GetUmbracoMvcArea(hostingEnvironment);
- //if its the normal /umbraco path
- if (urlPath.InvariantEquals("/" + mvcArea)
- || urlPath.InvariantEquals("/" + mvcArea + "/"))
- {
- return true;
- }
-
- //check for a file extension
- var extension = Path.GetExtension(url.LocalPath);
- //has an extension, def back office
- if (extension.IsNullOrWhiteSpace() == false) return true;
- //check for special case asp.net calls like:
- // /umbraco/webservices/legacyAjaxCalls.asmx/js which will return a null file extension but are still considered requests with an extension
- if (urlPath.InvariantContains(".asmx/")
- || urlPath.InvariantContains(".aspx/")
- || urlPath.InvariantContains(".ashx/")
- || urlPath.InvariantContains(".axd/")
- || urlPath.InvariantContains(".svc/"))
- {
- return true;
- }
-
- //check for special back office paths
- if (urlPath.InvariantStartsWith("/" + mvcArea + "/BackOffice/")
- || urlPath.InvariantStartsWith("/" + mvcArea + "/Preview/"))
- {
- return true;
- }
-
- //check for special front-end paths
- // TODO: These should be constants - will need to update when we do front-end routing
- if (urlPath.InvariantStartsWith("/" + mvcArea + "/Surface/")
- || urlPath.InvariantStartsWith("/" + mvcArea + "/Api/"))
- {
- return false;
- }
-
- //if its none of the above, we will have to try to detect if it's a PluginController route, we can detect this by
- // checking how many parts the route has, for example, all PluginController routes will be routed like
- // Umbraco/MYPLUGINAREA/MYCONTROLLERNAME/{action}/{id}
- // so if the path contains at a minimum 3 parts: Umbraco + MYPLUGINAREA + MYCONTROLLERNAME then we will have to assume it is a
- // plugin controller for the front-end.
- if (urlPath.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries).Length >= 3)
- {
- return false;
- }
-
- // if its anything else we can assume it's back office
- return true;
- }
-
- ///
- /// Checks if the current uri is an install request
- ///
- public static bool IsInstallerRequest(this Uri url, IHostingEnvironment hostingEnvironment)
- {
- var authority = url.GetLeftPart(UriPartial.Authority);
- var afterAuthority = url.GetLeftPart(UriPartial.Query)
- .TrimStart(authority)
- .TrimStart("/");
-
- // check if this is in the umbraco back office
- return afterAuthority.InvariantStartsWith(hostingEnvironment.ToAbsolute(Constants.SystemDirectories.Install).TrimStart("/"));
- }
-
- ///
- /// Checks if the uri is a request for the default back office page
- ///
- public static bool IsDefaultBackOfficeRequest(this Uri url, GlobalSettings globalSettings, IHostingEnvironment hostingEnvironment)
- {
- var backOfficePath = globalSettings.GetBackOfficePath(hostingEnvironment);
- if (url.AbsolutePath.InvariantEquals(backOfficePath.TrimEnd("/"))
- || url.AbsolutePath.InvariantEquals(backOfficePath.EnsureEndsWith('/'))
- || url.AbsolutePath.InvariantEquals(backOfficePath.EnsureEndsWith('/') + "Default")
- || url.AbsolutePath.InvariantEquals(backOfficePath.EnsureEndsWith('/') + "Default/"))
- {
- return true;
- }
-
- return false;
- }
-
- ///
- /// This is a performance tweak to check if this not an ASP.Net server file
- /// .Net will pass these requests through to the module when in integrated mode.
- /// We want to ignore all of these requests immediately.
- ///
- ///
- ///
- public static bool IsClientSideRequest(this Uri url)
- {
- try
- {
- var ext = Path.GetExtension(url.LocalPath);
- if (ext.IsNullOrWhiteSpace()) return false;
- var toInclude = new[] {".aspx", ".ashx", ".asmx", ".axd", ".svc"};
- return toInclude.Any(ext.InvariantEquals) == false;
- }
- catch (ArgumentException)
- {
- StaticApplicationLogging.Logger.LogDebug("Failed to determine if request was client side (invalid chars in path \"{Path}\"?)", url.LocalPath);
- return false;
- }
- }
-
///
/// Rewrites the path of uri.
///
diff --git a/src/Umbraco.ModelsBuilder.Embedded/LiveModelsProvider.cs b/src/Umbraco.ModelsBuilder.Embedded/LiveModelsProvider.cs
index b8488e0852..d7fc051500 100644
--- a/src/Umbraco.ModelsBuilder.Embedded/LiveModelsProvider.cs
+++ b/src/Umbraco.ModelsBuilder.Embedded/LiveModelsProvider.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Threading;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
@@ -11,6 +11,7 @@ using Umbraco.ModelsBuilder.Embedded.Building;
using Umbraco.Web.Cache;
using Umbraco.Core.Configuration.Models;
using Microsoft.Extensions.Options;
+using Umbraco.Extensions;
namespace Umbraco.ModelsBuilder.Embedded
{
@@ -115,12 +116,16 @@ namespace Umbraco.ModelsBuilder.Embedded
public void AppEndRequest(HttpContext context)
{
- var requestUri = new Uri(context.Request.GetEncodedUrl(), UriKind.RelativeOrAbsolute);
-
- if (requestUri.IsClientSideRequest())
+ if (context.Request.IsClientSideRequest())
+ {
return;
+ }
+
+ if (!IsEnabled)
+ {
+ return;
+ }
- if (!IsEnabled) return;
GenerateModelsIfRequested();
}
}
diff --git a/src/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs b/src/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs
index 91d05ba0df..d1450552c3 100644
--- a/src/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs
+++ b/src/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs
@@ -6,8 +6,10 @@ using Microsoft.Extensions.Options;
using Moq;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Hosting;
+using Umbraco.Core.Routing;
using Umbraco.Core.Security;
using Umbraco.Tests.Common;
+using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.Common.AspNetCore;
using Umbraco.Web.PublishedCache;
@@ -54,7 +56,8 @@ namespace Umbraco.Tests.UnitTests.TestHelpers.Objects
var snapshotService = new Mock();
snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(snapshot.Object);
- IHostingEnvironment hostingEnvironment = Mock.Of();
+ IHostingEnvironment hostingEnvironment = TestHelper.GetHostingEnvironment();
+
var backofficeSecurityAccessorMock = new Mock();
backofficeSecurityAccessorMock.Setup(x => x.BackOfficeSecurity).Returns(Mock.Of());
@@ -63,7 +66,7 @@ namespace Umbraco.Tests.UnitTests.TestHelpers.Objects
snapshotService.Object,
new TestVariationContextAccessor(),
new TestDefaultCultureAccessor(),
- Options.Create(globalSettings),
+ new UmbracoRequestPaths(Options.Create(globalSettings), hostingEnvironment),
hostingEnvironment,
new UriUtility(hostingEnvironment),
new AspNetCoreCookieManager(httpContextAccessor),
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/UriExtensionsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/UriExtensionsTests.cs
index a072a1a189..fc8ecd0474 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/UriExtensionsTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/UriExtensionsTests.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Umbraco.
+// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
@@ -25,49 +25,6 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Extensions
private IWebHostEnvironment _hostEnvironment;
private GlobalSettings _globalSettings;
- [TestCase("http://www.domain.com/umbraco/preview/frame?id=1234", "", true)]
- [TestCase("http://www.domain.com/umbraco", "", true)]
- [TestCase("http://www.domain.com/Umbraco/", "", true)]
- [TestCase("http://www.domain.com/umbraco/default.aspx", "", true)]
- [TestCase("http://www.domain.com/umbraco/test/test", "", false)]
- [TestCase("http://www.domain.com/umbraco/test/test/test", "", false)]
- [TestCase("http://www.domain.com/Umbraco/test/test.aspx", "", true)]
- [TestCase("http://www.domain.com/umbraco/test/test.js", "", true)]
- [TestCase("http://www.domain.com/umbrac", "", false)]
- [TestCase("http://www.domain.com/test", "", false)]
- [TestCase("http://www.domain.com/test/umbraco", "", false)]
- [TestCase("http://www.domain.com/Umbraco/Backoffice/blah", "", true)]
- [TestCase("http://www.domain.com/Umbraco/anything", "", true)]
- [TestCase("http://www.domain.com/Umbraco/anything/", "", true)]
- [TestCase("http://www.domain.com/Umbraco/surface/blah", "", false)]
- [TestCase("http://www.domain.com/umbraco/api/blah", "", false)]
- [TestCase("http://www.domain.com/myvdir/umbraco/api/blah", "myvdir", false)]
- [TestCase("http://www.domain.com/MyVdir/umbraco/api/blah", "/myvdir", false)]
- [TestCase("http://www.domain.com/MyVdir/Umbraco/", "myvdir", true)]
- [TestCase("http://www.domain.com/umbraco/test/legacyAjaxCalls.ashx?some=query&blah=js", "", true)]
- public void Is_Back_Office_Request(string input, string virtualPath, bool expected)
- {
- var source = new Uri(input);
- var hostingEnvironment = CreateHostingEnvironment(virtualPath);
- Assert.AreEqual(expected, source.IsBackOfficeRequest(_globalSettings, hostingEnvironment));
- }
-
- [TestCase("http://www.domain.com/install", true)]
- [TestCase("http://www.domain.com/Install/", true)]
- [TestCase("http://www.domain.com/install/default.aspx", true)]
- [TestCase("http://www.domain.com/install/test/test", true)]
- [TestCase("http://www.domain.com/Install/test/test.aspx", true)]
- [TestCase("http://www.domain.com/install/test/test.js", true)]
- [TestCase("http://www.domain.com/instal", false)]
- [TestCase("http://www.domain.com/umbraco", false)]
- [TestCase("http://www.domain.com/umbraco/umbraco", false)]
- public void Is_Installer_Request(string input, bool expected)
- {
- var source = new Uri(input);
- var hostingEnvironment = CreateHostingEnvironment();
- Assert.AreEqual(expected, source.IsInstallerRequest(hostingEnvironment));
- }
-
private AspNetCoreHostingEnvironment CreateHostingEnvironment(string virtualPath = "")
{
var hostingSettings = new HostingSettings { ApplicationVirtualPath = virtualPath };
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UmbracoRequestPathsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UmbracoRequestPathsTests.cs
new file mode 100644
index 0000000000..24f0b04080
--- /dev/null
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UmbracoRequestPathsTests.cs
@@ -0,0 +1,105 @@
+using System;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Options;
+using Moq;
+using NUnit.Framework;
+using Umbraco.Core.Configuration.Models;
+using Umbraco.Core.Routing;
+using Umbraco.Web.Common.AspNetCore;
+using IHostingEnvironment = Umbraco.Core.Hosting.IHostingEnvironment;
+
+namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing
+{
+ [TestFixture]
+ public class UmbracoRequestPathsTests
+ {
+ private IWebHostEnvironment _hostEnvironment;
+ private GlobalSettings _globalSettings;
+
+ [OneTimeSetUp]
+ public void Setup()
+ {
+ _hostEnvironment = Mock.Of();
+ _globalSettings = new GlobalSettings();
+ }
+
+ private AspNetCoreHostingEnvironment CreateHostingEnvironment(string virtualPath = "")
+ {
+ var hostingSettings = new HostingSettings { ApplicationVirtualPath = virtualPath };
+ var mockedOptionsMonitorOfHostingSettings = Mock.Of>(x => x.CurrentValue == hostingSettings);
+ return new AspNetCoreHostingEnvironment(mockedOptionsMonitorOfHostingSettings, _hostEnvironment);
+ }
+
+ [TestCase("/favicon.ico", true)]
+ [TestCase("/umbraco_client/Tree/treeIcons.css", true)]
+ [TestCase("/umbraco_client/Tree/Themes/umbraco/style.css?cdv=37", true)]
+ [TestCase("/base/somebasehandler", false)]
+ [TestCase("/", false)]
+ [TestCase("/home.aspx", true)] // has ext, assume client side
+ [TestCase("http://www.domain.com/Umbraco/test/test.aspx", true)] // has ext, assume client side
+ [TestCase("http://www.domain.com/umbraco/test/test.js", true)]
+ public void Is_Client_Side_Request(string url, bool assert)
+ {
+ IHostingEnvironment hostingEnvironment = CreateHostingEnvironment();
+ var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment);
+
+ var uri = new Uri("http://test.com" + url);
+ var result = umbracoRequestPaths.IsClientSideRequest(uri.AbsolutePath);
+ Assert.AreEqual(assert, result);
+ }
+
+ [Test]
+ public void Is_Client_Side_Request_InvalidPath_ReturnFalse()
+ {
+ IHostingEnvironment hostingEnvironment = CreateHostingEnvironment();
+ var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment);
+
+ // This URL is invalid. Default to false when the extension cannot be determined
+ var uri = new Uri("http://test.com/installing-modules+foobar+\"yipee\"");
+ var result = umbracoRequestPaths.IsClientSideRequest(uri.AbsolutePath);
+ Assert.AreEqual(false, result);
+ }
+
+ [TestCase("http://www.domain.com/umbraco/preview/frame?id=1234", "", true)]
+ [TestCase("http://www.domain.com/umbraco", "", true)]
+ [TestCase("http://www.domain.com/Umbraco/", "", true)]
+ [TestCase("http://www.domain.com/umbraco/default.aspx", "", true)]
+ [TestCase("http://www.domain.com/umbraco/test/test", "", false)]
+ [TestCase("http://www.domain.com/umbraco/test/test/test", "", false)]
+ [TestCase("http://www.domain.com/umbrac", "", false)]
+ [TestCase("http://www.domain.com/test", "", false)]
+ [TestCase("http://www.domain.com/test/umbraco", "", false)]
+ [TestCase("http://www.domain.com/Umbraco/Backoffice/blah", "", true)]
+ [TestCase("http://www.domain.com/Umbraco/anything", "", true)]
+ [TestCase("http://www.domain.com/Umbraco/anything/", "", true)]
+ [TestCase("http://www.domain.com/Umbraco/surface/blah", "", false)]
+ [TestCase("http://www.domain.com/umbraco/api/blah", "", false)]
+ [TestCase("http://www.domain.com/myvdir/umbraco/api/blah", "myvdir", false)]
+ [TestCase("http://www.domain.com/MyVdir/umbraco/api/blah", "/myvdir", false)]
+ [TestCase("http://www.domain.com/MyVdir/Umbraco/", "myvdir", true)]
+ public void Is_Back_Office_Request(string input, string virtualPath, bool expected)
+ {
+ var source = new Uri(input);
+ var hostingEnvironment = CreateHostingEnvironment(virtualPath);
+ var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment);
+ Assert.AreEqual(expected, umbracoRequestPaths.IsBackOfficeRequest(source.AbsolutePath));
+ }
+
+ [TestCase("http://www.domain.com/install", true)]
+ [TestCase("http://www.domain.com/Install/", true)]
+ [TestCase("http://www.domain.com/install/default.aspx", true)]
+ [TestCase("http://www.domain.com/install/test/test", true)]
+ [TestCase("http://www.domain.com/Install/test/test.aspx", true)]
+ [TestCase("http://www.domain.com/install/test/test.js", true)]
+ [TestCase("http://www.domain.com/instal", false)]
+ [TestCase("http://www.domain.com/umbraco", false)]
+ [TestCase("http://www.domain.com/umbraco/umbraco", false)]
+ public void Is_Installer_Request(string input, bool expected)
+ {
+ var source = new Uri(input);
+ var hostingEnvironment = CreateHostingEnvironment();
+ var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment);
+ Assert.AreEqual(expected, umbracoRequestPaths.IsInstallerRequest(source.AbsolutePath));
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs
index b677f11f2c..974254179d 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs
@@ -1,13 +1,15 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
-using System;
+using Microsoft.Extensions.Options;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Hosting;
+using Umbraco.Core.Routing;
using Umbraco.Extensions;
+using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.BackOffice.Controllers;
using Umbraco.Web.BackOffice.Security;
@@ -26,10 +28,9 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Backoffice.Security
var mgr = new BackOfficeCookieManager(
Mock.Of(),
runtime,
- Mock.Of(),
- globalSettings);
+ new UmbracoRequestPaths(Options.Create(globalSettings), TestHelper.GetHostingEnvironment()));
- var result = mgr.ShouldAuthenticateRequest(new Uri("http://localhost/umbraco"));
+ var result = mgr.ShouldAuthenticateRequest("/umbraco");
Assert.IsFalse(result);
}
@@ -43,10 +44,11 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Backoffice.Security
var mgr = new BackOfficeCookieManager(
Mock.Of(),
runtime,
- Mock.Of(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco"),
- globalSettings);
+ new UmbracoRequestPaths(
+ Options.Create(globalSettings),
+ Mock.Of(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco")));
- var result = mgr.ShouldAuthenticateRequest(new Uri("http://localhost/umbraco"));
+ var result = mgr.ShouldAuthenticateRequest("/umbraco");
Assert.IsTrue(result);
}
@@ -63,13 +65,14 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Backoffice.Security
var mgr = new BackOfficeCookieManager(
Mock.Of(),
runtime,
- Mock.Of(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && x.ToAbsolute(Constants.SystemDirectories.Install) == "/install"),
- globalSettings);
+ new UmbracoRequestPaths(
+ Options.Create(globalSettings),
+ Mock.Of(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && x.ToAbsolute(Constants.SystemDirectories.Install) == "/install")));
- var result = mgr.ShouldAuthenticateRequest(new Uri($"http://localhost{remainingTimeoutSecondsPath}"));
+ var result = mgr.ShouldAuthenticateRequest(remainingTimeoutSecondsPath);
Assert.IsTrue(result);
- result = mgr.ShouldAuthenticateRequest(new Uri($"http://localhost{isAuthPath}"));
+ result = mgr.ShouldAuthenticateRequest(isAuthPath);
Assert.IsTrue(result);
}
@@ -83,14 +86,15 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Backoffice.Security
var mgr = new BackOfficeCookieManager(
Mock.Of(),
runtime,
- Mock.Of(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && x.ToAbsolute(Constants.SystemDirectories.Install) == "/install"),
- globalSettings);
+ new UmbracoRequestPaths(
+ Options.Create(globalSettings),
+ Mock.Of(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && x.ToAbsolute(Constants.SystemDirectories.Install) == "/install")));
- var result = mgr.ShouldAuthenticateRequest(new Uri($"http://localhost/notbackoffice"));
+ var result = mgr.ShouldAuthenticateRequest("/notbackoffice");
Assert.IsFalse(result);
- result = mgr.ShouldAuthenticateRequest(new Uri($"http://localhost/umbraco/api/notbackoffice"));
+ result = mgr.ShouldAuthenticateRequest("/umbraco/api/notbackoffice");
Assert.IsFalse(result);
- result = mgr.ShouldAuthenticateRequest(new Uri($"http://localhost/umbraco/surface/notbackoffice"));
+ result = mgr.ShouldAuthenticateRequest("/umbraco/surface/notbackoffice");
Assert.IsFalse(result);
}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/EndpointRouteBuilderExtensionsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/EndpointRouteBuilderExtensionsTests.cs
index f5b491a8af..0990cb9d9a 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/EndpointRouteBuilderExtensionsTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/EndpointRouteBuilderExtensionsTests.cs
@@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Routing;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Extensions;
-using Umbraco.Web.Common.Routing;
+using Umbraco.Web.Common.Extensions;
using Constants = Umbraco.Core.Constants;
namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Routing
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/RoutableDocumentFilterTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/RoutableDocumentFilterTests.cs
new file mode 100644
index 0000000000..1c9cbc9c26
--- /dev/null
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/RoutableDocumentFilterTests.cs
@@ -0,0 +1,144 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.AspNetCore.Routing.Patterns;
+using Microsoft.Extensions.Options;
+using Moq;
+using NUnit.Framework;
+using Umbraco.Core.Configuration.Models;
+using Umbraco.Core.Hosting;
+using Umbraco.Core.IO;
+using Umbraco.Web.Common.Routing;
+
+namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Routing
+{
+ [TestFixture]
+ public class RoutableDocumentFilterTests
+ {
+ private IOptions GetGlobalSettings() => Options.Create(new GlobalSettings());
+
+ private IOptions GetWebRoutingSettings() => Options.Create(new WebRoutingSettings());
+
+ private IHostingEnvironment GetHostingEnvironment()
+ {
+ var hostingEnv = new Mock();
+ hostingEnv.Setup(x => x.ToAbsolute(It.IsAny())).Returns((string virtualPath) => virtualPath.TrimStart('~', '/'));
+ return hostingEnv.Object;
+ }
+
+ [TestCase("/umbraco/editContent.aspx")]
+ [TestCase("/install/default.aspx")]
+ [TestCase("/install/")]
+ [TestCase("/install")]
+ [TestCase("/install/?installStep=asdf")]
+ [TestCase("/install/test.aspx")]
+ public void Is_Reserved_Path_Or_Url(string url)
+ {
+ var routableDocFilter = new RoutableDocumentFilter(
+ GetGlobalSettings(),
+ GetWebRoutingSettings(),
+ GetHostingEnvironment(),
+ new DefaultEndpointDataSource());
+
+ // Will be false if it is a reserved path
+ Assert.IsFalse(routableDocFilter.IsDocumentRequest(url));
+ }
+
+ [TestCase("/base/somebasehandler")]
+ [TestCase("/")]
+ [TestCase("/home")]
+ [TestCase("/umbraco-test")]
+ [TestCase("/install-test")]
+ public void Is_Not_Reserved_Path_Or_Url(string url)
+ {
+ var routableDocFilter = new RoutableDocumentFilter(
+ GetGlobalSettings(),
+ GetWebRoutingSettings(),
+ GetHostingEnvironment(),
+ new DefaultEndpointDataSource());
+
+ // Will be true if it's not reserved
+ Assert.IsTrue(routableDocFilter.IsDocumentRequest(url));
+ }
+
+ [TestCase("/Do/Not/match", false)]
+ [TestCase("/Umbraco/RenderMvcs", false)]
+ [TestCase("/Umbraco/RenderMvc", true)]
+ [TestCase("/umbraco/RenderMvc/Index", true)]
+ [TestCase("/Umbraco/RenderMvc/Index/1234", true)]
+ [TestCase("/Umbraco/RenderMvc/Index/1234/", true)]
+ [TestCase("/Umbraco/RenderMvc/Index/1234/9876", false)]
+ [TestCase("/api", true)]
+ [TestCase("/api/WebApiTest", true)]
+ [TestCase("/Api/WebApiTest/1234", true)]
+ [TestCase("/api/WebApiTest/Index/1234", false)]
+ public void Is_Reserved_By_Route(string url, bool isReserved)
+ {
+ var globalSettings = new GlobalSettings { ReservedPaths = string.Empty, ReservedUrls = string.Empty };
+ var routingSettings = new WebRoutingSettings { TryMatchingEndpointsForAllPages = true };
+
+ RouteEndpoint endpoint1 = CreateEndpoint(
+ "Umbraco/RenderMvc/{action?}/{id?}",
+ new { controller = "RenderMvc" },
+ 0);
+
+ RouteEndpoint endpoint2 = CreateEndpoint(
+ "api/{controller?}/{id?}",
+ new { action = "Index" },
+ 1);
+
+ var endpointDataSource = new DefaultEndpointDataSource(endpoint1, endpoint2);
+
+ var routableDocFilter = new RoutableDocumentFilter(
+ Options.Create(globalSettings),
+ Options.Create(routingSettings),
+ GetHostingEnvironment(),
+ endpointDataSource);
+
+ Assert.AreEqual(
+ !isReserved, // not reserved if it's a document request
+ routableDocFilter.IsDocumentRequest(url));
+ }
+
+ [TestCase("/umbraco", true)]
+ [TestCase("/umbraco/", true)]
+ [TestCase("/umbraco/Default", true)]
+ [TestCase("/umbraco/default/", true)]
+ [TestCase("/umbraco/default/123", true)]
+ [TestCase("/umbraco/default/blah/123", false)]
+ public void Is_Reserved_By_Default_Back_Office_Route(string url, bool isReserved)
+ {
+ var globalSettings = new GlobalSettings { ReservedPaths = string.Empty, ReservedUrls = string.Empty };
+ var routingSettings = new WebRoutingSettings { TryMatchingEndpointsForAllPages = true };
+
+ RouteEndpoint endpoint1 = CreateEndpoint(
+ "umbraco/{action?}/{id?}",
+ new { controller = "BackOffice" },
+ 0);
+
+ var endpointDataSource = new DefaultEndpointDataSource(endpoint1);
+
+ var routableDocFilter = new RoutableDocumentFilter(
+ Options.Create(globalSettings),
+ Options.Create(routingSettings),
+ GetHostingEnvironment(),
+ endpointDataSource);
+
+ Assert.AreEqual(
+ !isReserved, // not reserved if it's a document request
+ routableDocFilter.IsDocumentRequest(url));
+ }
+
+ // borrowed from https://github.com/dotnet/aspnetcore/blob/19559e73da2b6d335b864ed2855dd8a0c7a207a0/src/Mvc/Mvc.Core/test/Routing/ControllerLinkGeneratorExtensionsTest.cs#L171
+ private RouteEndpoint CreateEndpoint(
+ string template,
+ object defaults = null,
+ int order = 0) => new RouteEndpoint(
+ (httpContext) => Task.CompletedTask,
+ RoutePatternFactory.Parse(template, defaults, null),
+ order,
+ new EndpointMetadataCollection(Array.Empty