Merge branch 'netcore/netcore' into netcore/members-userstore
This commit is contained in:
@@ -10,6 +10,17 @@ namespace Umbraco.Core.Configuration.Models
|
||||
/// </summary>
|
||||
public class WebRoutingSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public bool TryMatchingEndpointsForAllPages { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether IIS custom errors should be skipped.
|
||||
/// </summary>
|
||||
|
||||
@@ -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<UmbracoApplicationStarting, EssentialDirectoryCreator>();
|
||||
|
||||
Services.AddSingleton<ManifestWatcher>();
|
||||
Services.AddSingleton<UmbracoRequestPaths>();
|
||||
this.AddNotificationHandler<UmbracoApplicationStarting, AppPluginsManifestWatcherNotificationHandler>();
|
||||
|
||||
Services.AddUnique<InstallStatusTracker>();
|
||||
|
||||
133
src/Umbraco.Core/Routing/UmbracoRequestPaths.cs
Normal file
133
src/Umbraco.Core/Routing/UmbracoRequestPaths.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility for checking paths
|
||||
/// </summary>
|
||||
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<string> _defaultUmbPaths;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UmbracoRequestPaths"/> class.
|
||||
/// </summary>
|
||||
public UmbracoRequestPaths(IOptions<GlobalSettings> 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<string> { "/" + _mvcArea, "/" + _mvcArea + "/" };
|
||||
_backOfficeMvcPath = "/" + _mvcArea + "/BackOffice/";
|
||||
_previewMvcPath = "/" + _mvcArea + "/Preview/";
|
||||
_surfaceMvcPath = "/" + _mvcArea + "/Surface/";
|
||||
_apiMvcPath = "/" + _mvcArea + "/Api/";
|
||||
_installPath = hostingEnvironment.ToAbsolute(Constants.SystemDirectories.Install);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the current uri is a back office request
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// There are some special routes we need to check to properly determine this:
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// These are def back office:
|
||||
/// /Umbraco/BackOffice = back office
|
||||
/// /Umbraco/Preview = back office
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// 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.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// 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.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the current uri is an install request
|
||||
/// </summary>
|
||||
public bool IsInstallerRequest(string absPath) => absPath.InvariantStartsWith(_installPath);
|
||||
|
||||
/// <summary>
|
||||
/// Rudimentary check to see if it's not a server side request
|
||||
/// </summary>
|
||||
public bool IsClientSideRequest(string absPath)
|
||||
{
|
||||
var ext = Path.GetExtension(absPath);
|
||||
return !ext.IsNullOrWhiteSpace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,151 +13,6 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public static class UriExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the current uri is a back office request
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="globalSettings"></param>
|
||||
/// <param name="hostingEnvironment"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
///
|
||||
/// </remarks>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the current uri is an install request
|
||||
/// </summary>
|
||||
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("/"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the uri is a request for the default back office page
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rewrites the path of uri.
|
||||
/// </summary>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<IPublishedSnapshotService>();
|
||||
snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny<string>())).Returns(snapshot.Object);
|
||||
|
||||
IHostingEnvironment hostingEnvironment = Mock.Of<IHostingEnvironment>();
|
||||
IHostingEnvironment hostingEnvironment = TestHelper.GetHostingEnvironment();
|
||||
|
||||
var backofficeSecurityAccessorMock = new Mock<IBackOfficeSecurityAccessor>();
|
||||
backofficeSecurityAccessorMock.Setup(x => x.BackOfficeSecurity).Returns(Mock.Of<IBackOfficeSecurity>());
|
||||
|
||||
@@ -63,7 +66,7 @@ namespace Umbraco.Tests.UnitTests.TestHelpers.Objects
|
||||
snapshotService.Object,
|
||||
new TestVariationContextAccessor(),
|
||||
new TestDefaultCultureAccessor(),
|
||||
Options.Create<GlobalSettings>(globalSettings),
|
||||
new UmbracoRequestPaths(Options.Create(globalSettings), hostingEnvironment),
|
||||
hostingEnvironment,
|
||||
new UriUtility(hostingEnvironment),
|
||||
new AspNetCoreCookieManager(httpContextAccessor),
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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<IWebHostEnvironment>();
|
||||
_globalSettings = new GlobalSettings();
|
||||
}
|
||||
|
||||
private AspNetCoreHostingEnvironment CreateHostingEnvironment(string virtualPath = "")
|
||||
{
|
||||
var hostingSettings = new HostingSettings { ApplicationVirtualPath = virtualPath };
|
||||
var mockedOptionsMonitorOfHostingSettings = Mock.Of<IOptionsMonitor<HostingSettings>>(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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IUmbracoContextAccessor>(),
|
||||
runtime,
|
||||
Mock.Of<IHostingEnvironment>(),
|
||||
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<IUmbracoContextAccessor>(),
|
||||
runtime,
|
||||
Mock.Of<IHostingEnvironment>(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco"),
|
||||
globalSettings);
|
||||
new UmbracoRequestPaths(
|
||||
Options.Create(globalSettings),
|
||||
Mock.Of<IHostingEnvironment>(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<IUmbracoContextAccessor>(),
|
||||
runtime,
|
||||
Mock.Of<IHostingEnvironment>(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && x.ToAbsolute(Constants.SystemDirectories.Install) == "/install"),
|
||||
globalSettings);
|
||||
new UmbracoRequestPaths(
|
||||
Options.Create(globalSettings),
|
||||
Mock.Of<IHostingEnvironment>(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<IUmbracoContextAccessor>(),
|
||||
runtime,
|
||||
Mock.Of<IHostingEnvironment>(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && x.ToAbsolute(Constants.SystemDirectories.Install) == "/install"),
|
||||
globalSettings);
|
||||
new UmbracoRequestPaths(
|
||||
Options.Create(globalSettings),
|
||||
Mock.Of<IHostingEnvironment>(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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<GlobalSettings> GetGlobalSettings() => Options.Create(new GlobalSettings());
|
||||
|
||||
private IOptions<WebRoutingSettings> GetWebRoutingSettings() => Options.Create(new WebRoutingSettings());
|
||||
|
||||
private IHostingEnvironment GetHostingEnvironment()
|
||||
{
|
||||
var hostingEnv = new Mock<IHostingEnvironment>();
|
||||
hostingEnv.Setup(x => x.ToAbsolute(It.IsAny<string>())).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<object>()),
|
||||
null);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Cache;
|
||||
@@ -16,13 +15,12 @@ using Umbraco.Core.Security;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Tests.Common;
|
||||
using Umbraco.Tests.Testing;
|
||||
using Umbraco.Tests.UnitTests.TestHelpers.Objects;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.Common.Routing;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Web.Website;
|
||||
using Umbraco.Web.Website.Controllers;
|
||||
using Umbraco.Web.Website.Routing;
|
||||
using CoreConstants = Umbraco.Core.Constants;
|
||||
|
||||
namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Controllers
|
||||
@@ -44,17 +42,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Controllers
|
||||
Mock.Get(backofficeSecurityAccessor).Setup(x => x.BackOfficeSecurity).Returns(Mock.Of<IBackOfficeSecurity>());
|
||||
var globalSettings = new GlobalSettings();
|
||||
|
||||
var umbracoContextFactory = new UmbracoContextFactory(
|
||||
_umbracoContextAccessor,
|
||||
Mock.Of<IPublishedSnapshotService>(),
|
||||
new TestVariationContextAccessor(),
|
||||
new TestDefaultCultureAccessor(),
|
||||
Options.Create(globalSettings),
|
||||
hostingEnvironment,
|
||||
new UriUtility(hostingEnvironment),
|
||||
Mock.Of<ICookieManager>(),
|
||||
Mock.Of<IRequestAccessor>(),
|
||||
backofficeSecurityAccessor);
|
||||
var umbracoContextFactory = TestUmbracoContextFactory.Create(globalSettings, _umbracoContextAccessor);
|
||||
|
||||
UmbracoContextReference umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext();
|
||||
IUmbracoContext umbracoContext = umbracoContextReference.UmbracoContext;
|
||||
@@ -75,17 +63,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Controllers
|
||||
IHostingEnvironment hostingEnvironment = Mock.Of<IHostingEnvironment>();
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor = Mock.Of<IBackOfficeSecurityAccessor>();
|
||||
Mock.Get(backofficeSecurityAccessor).Setup(x => x.BackOfficeSecurity).Returns(Mock.Of<IBackOfficeSecurity>());
|
||||
var umbracoContextFactory = new UmbracoContextFactory(
|
||||
_umbracoContextAccessor,
|
||||
Mock.Of<IPublishedSnapshotService>(),
|
||||
new TestVariationContextAccessor(),
|
||||
new TestDefaultCultureAccessor(),
|
||||
Options.Create(globalSettings),
|
||||
hostingEnvironment,
|
||||
new UriUtility(hostingEnvironment),
|
||||
Mock.Of<ICookieManager>(),
|
||||
Mock.Of<IRequestAccessor>(),
|
||||
backofficeSecurityAccessor);
|
||||
var umbracoContextFactory = TestUmbracoContextFactory.Create(globalSettings, _umbracoContextAccessor);
|
||||
|
||||
UmbracoContextReference umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext();
|
||||
IUmbracoContext umbCtx = umbracoContextReference.UmbracoContext;
|
||||
@@ -110,17 +88,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Controllers
|
||||
IHostingEnvironment hostingEnvironment = Mock.Of<IHostingEnvironment>();
|
||||
var globalSettings = new GlobalSettings();
|
||||
|
||||
var umbracoContextFactory = new UmbracoContextFactory(
|
||||
_umbracoContextAccessor,
|
||||
publishedSnapshotService.Object,
|
||||
new TestVariationContextAccessor(),
|
||||
new TestDefaultCultureAccessor(),
|
||||
Options.Create(globalSettings),
|
||||
hostingEnvironment,
|
||||
new UriUtility(hostingEnvironment),
|
||||
Mock.Of<ICookieManager>(),
|
||||
Mock.Of<IRequestAccessor>(),
|
||||
backofficeSecurityAccessor);
|
||||
var umbracoContextFactory = TestUmbracoContextFactory.Create(globalSettings, _umbracoContextAccessor);
|
||||
|
||||
UmbracoContextReference umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext();
|
||||
IUmbracoContext umbracoContext = umbracoContextReference.UmbracoContext;
|
||||
@@ -144,17 +112,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Controllers
|
||||
IHostingEnvironment hostingEnvironment = Mock.Of<IHostingEnvironment>();
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor = Mock.Of<IBackOfficeSecurityAccessor>();
|
||||
Mock.Get(backofficeSecurityAccessor).Setup(x => x.BackOfficeSecurity).Returns(Mock.Of<IBackOfficeSecurity>());
|
||||
var umbracoContextFactory = new UmbracoContextFactory(
|
||||
_umbracoContextAccessor,
|
||||
Mock.Of<IPublishedSnapshotService>(),
|
||||
new TestVariationContextAccessor(),
|
||||
new TestDefaultCultureAccessor(),
|
||||
Options.Create(globalSettings),
|
||||
hostingEnvironment,
|
||||
new UriUtility(hostingEnvironment),
|
||||
Mock.Of<ICookieManager>(),
|
||||
Mock.Of<IRequestAccessor>(),
|
||||
backofficeSecurityAccessor);
|
||||
var umbracoContextFactory = TestUmbracoContextFactory.Create(globalSettings, _umbracoContextAccessor);
|
||||
|
||||
UmbracoContextReference umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext();
|
||||
IUmbracoContext umbracoContext = umbracoContextReference.UmbracoContext;
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
using System;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Tests.Common.Builders;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Tests.Routing
|
||||
{
|
||||
[TestFixture]
|
||||
public class RoutableDocumentFilterTests : BaseWebTest
|
||||
{
|
||||
[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 globalSettings = TestObjects.GetGlobalSettings();
|
||||
var routableDocFilter = new RoutableDocumentFilter(globalSettings, IOHelper);
|
||||
Assert.IsTrue(routableDocFilter.IsReservedPathOrUrl(url));
|
||||
}
|
||||
|
||||
[TestCase("/base/somebasehandler")]
|
||||
[TestCase("/")]
|
||||
[TestCase("/home.aspx")]
|
||||
[TestCase("/umbraco-test")]
|
||||
[TestCase("/install-test")]
|
||||
[TestCase("/install.aspx")]
|
||||
public void Is_Not_Reserved_Path_Or_Url(string url)
|
||||
{
|
||||
var globalSettings = TestObjects.GetGlobalSettings();
|
||||
var routableDocFilter = new RoutableDocumentFilter(globalSettings, IOHelper);
|
||||
Assert.IsFalse(routableDocFilter.IsReservedPathOrUrl(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/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 shouldMatch)
|
||||
{
|
||||
//reset the app config, we only want to test routes not the hard coded paths
|
||||
|
||||
var globalSettings = new GlobalSettings { ReservedPaths = string.Empty, ReservedUrls = string.Empty };
|
||||
|
||||
var routableDocFilter = new RoutableDocumentFilter(globalSettings, IOHelper);
|
||||
|
||||
var routes = new RouteCollection();
|
||||
|
||||
routes.MapRoute(
|
||||
"Umbraco_default",
|
||||
"Umbraco/RenderMvc/{action}/{id}",
|
||||
new { controller = "RenderMvc", action = "Index", id = UrlParameter.Optional });
|
||||
routes.MapRoute(
|
||||
"WebAPI",
|
||||
"api/{controller}/{id}",
|
||||
new { controller = "WebApiTestController", action = "Index", id = UrlParameter.Optional });
|
||||
|
||||
|
||||
var context = new FakeHttpContextFactory(url);
|
||||
|
||||
|
||||
Assert.AreEqual(
|
||||
shouldMatch,
|
||||
routableDocFilter.IsReservedPathOrUrl(url, context.HttpContext, routes));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,9 +32,7 @@ namespace Umbraco.Tests.Routing
|
||||
(
|
||||
runtime,
|
||||
logger,
|
||||
null, // FIXME: PublishedRouter complexities...
|
||||
Mock.Of<IUmbracoContextFactory>(),
|
||||
new RoutableDocumentFilter(globalSettings, IOHelper),
|
||||
globalSettings,
|
||||
HostingEnvironment
|
||||
);
|
||||
@@ -78,28 +76,6 @@ namespace Umbraco.Tests.Routing
|
||||
Assert.AreEqual(assert, result.Success);
|
||||
}
|
||||
|
||||
[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", false)]
|
||||
public void Is_Client_Side_Request(string url, bool assert)
|
||||
{
|
||||
var uri = new Uri("http://test.com" + url);
|
||||
var result = uri.IsClientSideRequest();
|
||||
Assert.AreEqual(assert, result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Is_Client_Side_Request_InvalidPath_ReturnFalse()
|
||||
{
|
||||
//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 = uri.IsClientSideRequest();
|
||||
Assert.AreEqual(false, result);
|
||||
}
|
||||
|
||||
//NOTE: This test shows how we can test most of the HttpModule, it however is testing a method that no longer exists and is testing too much,
|
||||
// we need to write unit tests for each of the components: NiceUrlProvider, all of the Lookup classes, etc...
|
||||
// to ensure that each one is individually tested.
|
||||
|
||||
@@ -150,7 +150,6 @@
|
||||
<Compile Include="PublishedContent\PublishedContentSnapshotTestBase.cs" />
|
||||
<Compile Include="PublishedContent\NuCacheTests.cs" />
|
||||
<Compile Include="Routing\MediaUrlProviderTests.cs" />
|
||||
<Compile Include="Routing\RoutableDocumentFilterTests.cs" />
|
||||
<Compile Include="Routing\GetContentUrlsTests.cs" />
|
||||
<Compile Include="TestHelpers\RandomIdRamDirectory.cs" />
|
||||
<Compile Include="TestHelpers\Stubs\TestUserPasswordConfig.cs" />
|
||||
|
||||
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Filters
|
||||
{
|
||||
@@ -21,9 +22,8 @@ namespace Umbraco.Web.BackOffice.Filters
|
||||
|
||||
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
|
||||
{
|
||||
var requestUri = new Uri(context.Request.GetEncodedUrl(), UriKind.RelativeOrAbsolute);
|
||||
// If it's a client side request just call next and don't try to log anything
|
||||
if (requestUri.IsClientSideRequest())
|
||||
if (context.Request.IsClientSideRequest())
|
||||
{
|
||||
await next(context);
|
||||
}
|
||||
@@ -36,7 +36,7 @@ namespace Umbraco.Web.BackOffice.Filters
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Unhandled controller exception occurred for request '{RequestUrl}'", requestUri.AbsoluteUri);
|
||||
_logger.LogError(e, "Unhandled controller exception occurred for request '{RequestUrl}'", context.Request.GetEncodedPathAndQuery());
|
||||
// Throw the error again, just in case it gets handled
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Middleware
|
||||
@@ -17,17 +14,7 @@ namespace Umbraco.Web.BackOffice.Middleware
|
||||
/// </summary>
|
||||
public class PreviewAuthenticationMiddleware : IMiddleware
|
||||
{
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
|
||||
public PreviewAuthenticationMiddleware(
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
IHostingEnvironment hostingEnvironment)
|
||||
{
|
||||
_globalSettings = globalSettings.Value;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
|
||||
{
|
||||
var request = context.Request;
|
||||
@@ -35,7 +22,7 @@ namespace Umbraco.Web.BackOffice.Middleware
|
||||
{
|
||||
var isPreview = request.HasPreviewCookie()
|
||||
&& context.User != null
|
||||
&& !request.IsBackOfficeRequest(_globalSettings, _hostingEnvironment);
|
||||
&& !request.IsBackOfficeRequest();
|
||||
|
||||
if (isPreview)
|
||||
{
|
||||
@@ -43,7 +30,9 @@ namespace Umbraco.Web.BackOffice.Middleware
|
||||
.Get(Constants.Security.BackOfficeAuthenticationType);
|
||||
|
||||
if (cookieOptions == null)
|
||||
{
|
||||
throw new InvalidOperationException("No cookie options found with name " + Constants.Security.BackOfficeAuthenticationType);
|
||||
}
|
||||
|
||||
// If we've gotten this far it means a preview cookie has been set and a front-end umbraco document request is executing.
|
||||
// In this case, authentication will not have occurred for an Umbraco back office User, however we need to perform the authentication
|
||||
@@ -55,11 +44,12 @@ namespace Umbraco.Web.BackOffice.Middleware
|
||||
{
|
||||
var backOfficeIdentity = unprotected.Principal.GetUmbracoIdentity();
|
||||
if (backOfficeIdentity != null)
|
||||
{
|
||||
// Ok, we've got a real ticket, now we can add this ticket's identity to the current
|
||||
// Principal, this means we'll have 2 identities assigned to the principal which we can
|
||||
// use to authorize the preview and allow for a back office User.
|
||||
|
||||
context.User.AddIdentity(backOfficeIdentity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
@@ -6,6 +6,7 @@ using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Web.BackOffice.Controllers;
|
||||
using Umbraco.Web.Common.Controllers;
|
||||
using Umbraco.Web.Common.Extensions;
|
||||
using Umbraco.Web.Common.Routing;
|
||||
using Umbraco.Web.WebApi;
|
||||
|
||||
@@ -61,14 +62,16 @@ namespace Umbraco.Web.BackOffice.Routing
|
||||
/// <summary>
|
||||
/// Map the minimal routes required to load the back office login and auth
|
||||
/// </summary>
|
||||
/// <param name="endpoints"></param>
|
||||
private void MapMinimalBackOffice(IEndpointRouteBuilder endpoints)
|
||||
{
|
||||
endpoints.MapUmbracoRoute<BackOfficeController>(_umbracoPathSegment, Constants.Web.Mvc.BackOfficeArea,
|
||||
endpoints.MapUmbracoRoute<BackOfficeController>(
|
||||
_umbracoPathSegment,
|
||||
Constants.Web.Mvc.BackOfficeArea,
|
||||
string.Empty,
|
||||
"Default",
|
||||
includeControllerNameInRoute: false,
|
||||
constraints:
|
||||
|
||||
// Limit the action/id to only allow characters - this is so this route doesn't hog all other
|
||||
// routes like: /umbraco/channels/word.aspx, etc...
|
||||
// (Not that we have to worry about too many of those these days, there still might be a need for these constraints).
|
||||
|
||||
@@ -7,6 +7,7 @@ using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Web.BackOffice.Controllers;
|
||||
using Umbraco.Web.BackOffice.SignalR;
|
||||
using Umbraco.Web.Common.Extensions;
|
||||
using Umbraco.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Routing
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Core.Routing;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Security
|
||||
{
|
||||
@@ -23,9 +18,8 @@ namespace Umbraco.Web.BackOffice.Security
|
||||
{
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly IRuntimeState _runtime;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly string[] _explicitPaths;
|
||||
private readonly UmbracoRequestPaths _umbracoRequestPaths;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BackOfficeCookieManager"/> class.
|
||||
@@ -33,10 +27,10 @@ namespace Umbraco.Web.BackOffice.Security
|
||||
public BackOfficeCookieManager(
|
||||
IUmbracoContextAccessor umbracoContextAccessor,
|
||||
IRuntimeState runtime,
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
GlobalSettings globalSettings)
|
||||
: this(umbracoContextAccessor, runtime, hostingEnvironment, globalSettings, null)
|
||||
{ }
|
||||
UmbracoRequestPaths umbracoRequestPaths)
|
||||
: this(umbracoContextAccessor, runtime, null, umbracoRequestPaths)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BackOfficeCookieManager"/> class.
|
||||
@@ -44,21 +38,18 @@ namespace Umbraco.Web.BackOffice.Security
|
||||
public BackOfficeCookieManager(
|
||||
IUmbracoContextAccessor umbracoContextAccessor,
|
||||
IRuntimeState runtime,
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
GlobalSettings globalSettings,
|
||||
IEnumerable<string> explicitPaths)
|
||||
IEnumerable<string> explicitPaths,
|
||||
UmbracoRequestPaths umbracoRequestPaths)
|
||||
{
|
||||
_umbracoContextAccessor = umbracoContextAccessor;
|
||||
_runtime = runtime;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_globalSettings = globalSettings;
|
||||
_explicitPaths = explicitPaths?.ToArray();
|
||||
_umbracoRequestPaths = umbracoRequestPaths;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if we should authenticate the request
|
||||
/// </summary>
|
||||
/// <param name="requestUri">The <see cref="Uri"/> to check</param>
|
||||
/// <returns>true if the request should be authenticated</returns>
|
||||
/// <remarks>
|
||||
/// We auth the request when:
|
||||
@@ -66,7 +57,7 @@ namespace Umbraco.Web.BackOffice.Security
|
||||
/// * it is an installer request
|
||||
/// * it is a preview request
|
||||
/// </remarks>
|
||||
public bool ShouldAuthenticateRequest(Uri requestUri)
|
||||
public bool ShouldAuthenticateRequest(string absPath)
|
||||
{
|
||||
// Do not authenticate the request if we are not running (don't have a db, are not configured) - since we will never need
|
||||
// to know a current user in this scenario - we treat it as a new install. Without this we can have some issues
|
||||
@@ -82,14 +73,14 @@ namespace Umbraco.Web.BackOffice.Security
|
||||
// check the explicit paths
|
||||
if (_explicitPaths != null)
|
||||
{
|
||||
return _explicitPaths.Any(x => x.InvariantEquals(requestUri.AbsolutePath));
|
||||
return _explicitPaths.Any(x => x.InvariantEquals(absPath));
|
||||
}
|
||||
|
||||
if (// check back office
|
||||
requestUri.IsBackOfficeRequest(_globalSettings, _hostingEnvironment)
|
||||
_umbracoRequestPaths.IsBackOfficeRequest(absPath)
|
||||
|
||||
// check installer
|
||||
|| requestUri.IsInstallerRequest(_hostingEnvironment))
|
||||
|| _umbracoRequestPaths.IsInstallerRequest(absPath))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -103,16 +94,18 @@ namespace Umbraco.Web.BackOffice.Security
|
||||
/// <inheritdoc/>
|
||||
string Microsoft.AspNetCore.Authentication.Cookies.ICookieManager.GetRequestCookie(HttpContext context, string key)
|
||||
{
|
||||
var requestUri = new Uri(context.Request.GetEncodedUrl(), UriKind.RelativeOrAbsolute);
|
||||
var absPath = context.Request.Path;
|
||||
|
||||
if (_umbracoContextAccessor.UmbracoContext == null || requestUri.IsClientSideRequest())
|
||||
if (_umbracoContextAccessor.UmbracoContext == null || _umbracoRequestPaths.IsClientSideRequest(absPath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ShouldAuthenticateRequest(requestUri) == false
|
||||
return ShouldAuthenticateRequest(absPath) == false
|
||||
|
||||
// Don't auth request, don't return a cookie
|
||||
? null
|
||||
|
||||
// Return the default implementation
|
||||
: GetRequestCookie(context, key);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Security.Claims;
|
||||
@@ -8,15 +10,12 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Infrastructure.Security;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Security
|
||||
{
|
||||
using ICookieManager = Microsoft.AspNetCore.Authentication.Cookies.ICookieManager;
|
||||
|
||||
/// <summary>
|
||||
/// Used to validate a cookie against a user's session id
|
||||
/// </summary>
|
||||
@@ -37,21 +36,24 @@ namespace Umbraco.Web.BackOffice.Security
|
||||
public const string CookieName = "UMB_UCONTEXT_C";
|
||||
private readonly ISystemClock _systemClock;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly IBackOfficeUserManager _userManager;
|
||||
|
||||
public BackOfficeSessionIdValidator(ISystemClock systemClock, IOptions<GlobalSettings> globalSettings, IHostingEnvironment hostingEnvironment, IBackOfficeUserManager userManager)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BackOfficeSessionIdValidator"/> class.
|
||||
/// </summary>
|
||||
public BackOfficeSessionIdValidator(ISystemClock systemClock, IOptions<GlobalSettings> globalSettings, IBackOfficeUserManager userManager)
|
||||
{
|
||||
_systemClock = systemClock;
|
||||
_globalSettings = globalSettings.Value;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public async Task ValidateSessionAsync(TimeSpan validateInterval, CookieValidatePrincipalContext context)
|
||||
{
|
||||
if (!context.Request.IsBackOfficeRequest(_globalSettings, _hostingEnvironment))
|
||||
if (!context.Request.IsBackOfficeRequest())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var valid = await ValidateSessionAsync(validateInterval, context.HttpContext, context.Options.CookieManager, _systemClock, context.Properties.IssuedUtc, context.Principal.Identity as ClaimsIdentity);
|
||||
|
||||
@@ -65,7 +67,7 @@ namespace Umbraco.Web.BackOffice.Security
|
||||
private async Task<bool> ValidateSessionAsync(
|
||||
TimeSpan validateInterval,
|
||||
HttpContext httpContext,
|
||||
ICookieManager cookieManager,
|
||||
Microsoft.AspNetCore.Authentication.Cookies.ICookieManager cookieManager,
|
||||
ISystemClock systemClock,
|
||||
DateTimeOffset? authTicketIssueDate,
|
||||
ClaimsIdentity currentIdentity)
|
||||
@@ -82,7 +84,7 @@ namespace Umbraco.Web.BackOffice.Security
|
||||
DateTimeOffset? issuedUtc = null;
|
||||
var currentUtc = systemClock.UtcNow;
|
||||
|
||||
//read the last checked time from a custom cookie
|
||||
// read the last checked time from a custom cookie
|
||||
var lastCheckedCookie = cookieManager.GetRequestCookie(httpContext, CookieName);
|
||||
|
||||
if (lastCheckedCookie.IsNullOrWhiteSpace() == false)
|
||||
@@ -93,7 +95,7 @@ namespace Umbraco.Web.BackOffice.Security
|
||||
}
|
||||
}
|
||||
|
||||
//no cookie, use the issue time of the auth ticket
|
||||
// no cookie, use the issue time of the auth ticket
|
||||
if (issuedUtc.HasValue == false)
|
||||
{
|
||||
issuedUtc = authTicketIssueDate;
|
||||
@@ -108,18 +110,24 @@ namespace Umbraco.Web.BackOffice.Security
|
||||
}
|
||||
|
||||
if (validate == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var userId = currentIdentity.GetUserId();
|
||||
var user = await _userManager.FindByIdAsync(userId);
|
||||
if (user == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var sessionId = currentIdentity.FindFirstValue(Constants.Security.SessionIdClaimType);
|
||||
if (await _userManager.ValidateSessionIdAsync(userId, sessionId) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//we will re-issue the cookie last checked cookie
|
||||
// we will re-issue the cookie last checked cookie
|
||||
cookieManager.AppendResponseCookie(
|
||||
httpContext,
|
||||
CookieName,
|
||||
|
||||
@@ -13,6 +13,7 @@ using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Routing;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
@@ -36,6 +37,7 @@ namespace Umbraco.Web.BackOffice.Security
|
||||
private readonly IUserService _userService;
|
||||
private readonly IIpResolver _ipResolver;
|
||||
private readonly ISystemClock _systemClock;
|
||||
private readonly UmbracoRequestPaths _umbracoRequestPaths;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConfigureBackOfficeCookieOptions"/> class.
|
||||
@@ -60,7 +62,8 @@ namespace Umbraco.Web.BackOffice.Security
|
||||
IDataProtectionProvider dataProtection,
|
||||
IUserService userService,
|
||||
IIpResolver ipResolver,
|
||||
ISystemClock systemClock)
|
||||
ISystemClock systemClock,
|
||||
UmbracoRequestPaths umbracoRequestPaths)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_umbracoContextAccessor = umbracoContextAccessor;
|
||||
@@ -72,6 +75,7 @@ namespace Umbraco.Web.BackOffice.Security
|
||||
_userService = userService;
|
||||
_ipResolver = ipResolver;
|
||||
_systemClock = systemClock;
|
||||
_umbracoRequestPaths = umbracoRequestPaths;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -115,8 +119,7 @@ namespace Umbraco.Web.BackOffice.Security
|
||||
options.CookieManager = new BackOfficeCookieManager(
|
||||
_umbracoContextAccessor,
|
||||
_runtimeState,
|
||||
_hostingEnvironment,
|
||||
_globalSettings); // _explicitPaths); TODO: Implement this once we do OAuth somehow
|
||||
_umbracoRequestPaths);
|
||||
|
||||
options.Events = new CookieAuthenticationEvents
|
||||
{
|
||||
|
||||
@@ -1,26 +1,18 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using System;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using NUglify.Helpers;
|
||||
using System;
|
||||
using System.Text;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Web.Common.Routing
|
||||
namespace Umbraco.Web.Common.Extensions
|
||||
{
|
||||
public static class EndpointRouteBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to map Umbraco controllers consistently
|
||||
/// </summary>
|
||||
/// <param name="endpoints"></param>
|
||||
/// <param name="controllerType"></param>
|
||||
/// <param name="rootSegment"></param>
|
||||
/// <param name="areaName"></param>
|
||||
/// <param name="prefixPathSegment"></param>
|
||||
/// <param name="defaultAction"></param>
|
||||
/// <param name="includeControllerNameInRoute"></param>
|
||||
/// <param name="constraints"></param>
|
||||
public static void MapUmbracoRoute(
|
||||
this IEndpointRouteBuilder endpoints,
|
||||
Type controllerType,
|
||||
@@ -36,19 +28,26 @@ namespace Umbraco.Web.Common.Routing
|
||||
// build the route pattern
|
||||
var pattern = new StringBuilder(rootSegment);
|
||||
if (!prefixPathSegment.IsNullOrWhiteSpace())
|
||||
{
|
||||
pattern.Append("/").Append(prefixPathSegment);
|
||||
}
|
||||
|
||||
if (includeControllerNameInRoute)
|
||||
{
|
||||
pattern.Append("/").Append(controllerName);
|
||||
}
|
||||
|
||||
pattern.Append("/").Append("{action}/{id?}");
|
||||
|
||||
var defaults = defaultAction.IsNullOrWhiteSpace()
|
||||
? (object) new { controller = controllerName }
|
||||
? (object)new { controller = controllerName }
|
||||
: new { controller = controllerName, action = defaultAction };
|
||||
|
||||
|
||||
if (areaName.IsNullOrWhiteSpace())
|
||||
{
|
||||
endpoints.MapControllerRoute(
|
||||
|
||||
// named consistently
|
||||
$"umbraco-{areaName}-{controllerName}".ToLowerInvariant(),
|
||||
pattern.ToString().ToLowerInvariant(),
|
||||
@@ -58,6 +57,7 @@ namespace Umbraco.Web.Common.Routing
|
||||
else
|
||||
{
|
||||
endpoints.MapAreaControllerRoute(
|
||||
|
||||
// named consistently
|
||||
$"umbraco-{areaName}-{controllerName}".ToLowerInvariant(),
|
||||
areaName,
|
||||
@@ -65,19 +65,11 @@ namespace Umbraco.Web.Common.Routing
|
||||
defaults,
|
||||
constraints);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to map Umbraco controllers consistently
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="endpoints"></param>
|
||||
/// <param name="rootSegment"></param>
|
||||
/// <param name="areaName"></param>
|
||||
/// <param name="prefixPathSegment"></param>
|
||||
/// <param name="defaultAction"></param>
|
||||
/// <param name="constraints"></param>
|
||||
public static void MapUmbracoRoute<T>(
|
||||
this IEndpointRouteBuilder endpoints,
|
||||
string rootSegment,
|
||||
@@ -92,12 +84,6 @@ namespace Umbraco.Web.Common.Routing
|
||||
/// <summary>
|
||||
/// Used to map Umbraco api controllers consistently
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="endpoints"></param>
|
||||
/// <param name="rootSegment"></param>
|
||||
/// <param name="areaName"></param>
|
||||
/// <param name="isBackOffice">If the route is a back office route</param>
|
||||
/// <param name="constraints"></param>
|
||||
public static void MapUmbracoApiRoute<T>(
|
||||
this IEndpointRouteBuilder endpoints,
|
||||
string rootSegment,
|
||||
@@ -111,13 +97,6 @@ namespace Umbraco.Web.Common.Routing
|
||||
/// <summary>
|
||||
/// Used to map Umbraco api controllers consistently
|
||||
/// </summary>
|
||||
/// <param name="endpoints"></param>
|
||||
/// <param name="controllerType"></param>
|
||||
/// <param name="rootSegment"></param>
|
||||
/// <param name="areaName"></param>
|
||||
/// <param name="isBackOffice">If the route is a back office route</param>
|
||||
/// <param name="defaultAction"></param>
|
||||
/// <param name="constraints"></param>
|
||||
public static void MapUmbracoApiRoute(
|
||||
this IEndpointRouteBuilder endpoints,
|
||||
Type controllerType,
|
||||
@@ -126,10 +105,23 @@ namespace Umbraco.Web.Common.Routing
|
||||
bool isBackOffice,
|
||||
string defaultAction = "Index",
|
||||
object constraints = null)
|
||||
=> endpoints.MapUmbracoRoute(controllerType, rootSegment, areaName,
|
||||
isBackOffice
|
||||
? (areaName.IsNullOrWhiteSpace() ? $"{Core.Constants.Web.Mvc.BackOfficePathSegment}/Api" : $"{Core.Constants.Web.Mvc.BackOfficePathSegment}/{areaName}")
|
||||
: (areaName.IsNullOrWhiteSpace() ? "Api" : areaName),
|
||||
defaultAction, true, constraints);
|
||||
{
|
||||
string prefixPathSegment = isBackOffice
|
||||
? areaName.IsNullOrWhiteSpace()
|
||||
? $"{Core.Constants.Web.Mvc.BackOfficePathSegment}/Api"
|
||||
: $"{Core.Constants.Web.Mvc.BackOfficePathSegment}/{areaName}"
|
||||
: areaName.IsNullOrWhiteSpace()
|
||||
? "Api"
|
||||
: areaName;
|
||||
|
||||
endpoints.MapUmbracoRoute(
|
||||
controllerType,
|
||||
rootSegment,
|
||||
areaName,
|
||||
prefixPathSegment,
|
||||
defaultAction,
|
||||
true,
|
||||
constraints);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,44 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Routing;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="HttpRequest"/>
|
||||
/// </summary>
|
||||
public static class HttpRequestExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Check if a preview cookie exist
|
||||
/// </summary>
|
||||
public static bool HasPreviewCookie(this HttpRequest request)
|
||||
=> request.Cookies.TryGetValue(Constants.Web.PreviewCookieName, out var cookieVal) && !cookieVal.IsNullOrWhiteSpace();
|
||||
|
||||
public static bool IsBackOfficeRequest(this HttpRequest request, GlobalSettings globalSettings, IHostingEnvironment hostingEnvironment)
|
||||
=> new Uri(request.GetEncodedUrl(), UriKind.RelativeOrAbsolute).IsBackOfficeRequest(globalSettings, hostingEnvironment);
|
||||
/// <summary>
|
||||
/// Returns true if the request is a back office request
|
||||
/// </summary>
|
||||
public static bool IsBackOfficeRequest(this HttpRequest request)
|
||||
{
|
||||
PathString absPath = request.Path;
|
||||
UmbracoRequestPaths umbReqPaths = request.HttpContext.RequestServices.GetService<UmbracoRequestPaths>();
|
||||
return umbReqPaths.IsBackOfficeRequest(absPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the request is for a client side extension
|
||||
/// </summary>
|
||||
public static bool IsClientSideRequest(this HttpRequest request)
|
||||
=> new Uri(request.GetEncodedUrl(), UriKind.RelativeOrAbsolute).IsClientSideRequest();
|
||||
|
||||
public static bool IsDefaultBackOfficeRequest(this HttpRequest request, GlobalSettings globalSettings, IHostingEnvironment hostingEnvironment)
|
||||
=> new Uri(request.GetEncodedUrl(), UriKind.RelativeOrAbsolute).IsDefaultBackOfficeRequest(globalSettings, hostingEnvironment);
|
||||
{
|
||||
PathString absPath = request.Path;
|
||||
UmbracoRequestPaths umbReqPaths = request.HttpContext.RequestServices.GetService<UmbracoRequestPaths>();
|
||||
return umbReqPaths.IsClientSideRequest(absPath);
|
||||
}
|
||||
|
||||
public static string ClientCulture(this HttpRequest request)
|
||||
=> request.Headers.TryGetValue("X-UMB-CULTURE", out var values) ? values[0] : null;
|
||||
|
||||
@@ -7,6 +7,7 @@ using Umbraco.Core;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.Common.Extensions;
|
||||
using Umbraco.Web.Common.Routing;
|
||||
|
||||
namespace Umbraco.Web.Common.Install
|
||||
|
||||
@@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.Common.Lifetime;
|
||||
using Umbraco.Web.PublishedCache.NuCache;
|
||||
|
||||
@@ -59,10 +60,8 @@ namespace Umbraco.Web.Common.Middleware
|
||||
/// <inheritdoc/>
|
||||
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
|
||||
{
|
||||
var requestUri = new Uri(context.Request.GetEncodedUrl(), UriKind.RelativeOrAbsolute);
|
||||
|
||||
// do not process if client-side request
|
||||
if (requestUri.IsClientSideRequest())
|
||||
if (context.Request.IsClientSideRequest())
|
||||
{
|
||||
await next(context);
|
||||
return;
|
||||
@@ -75,12 +74,14 @@ namespace Umbraco.Web.Common.Middleware
|
||||
|
||||
bool isFrontEndRequest = umbracoContextReference.UmbracoContext.IsFrontEndUmbracoRequest();
|
||||
|
||||
var pathAndQuery = context.Request.GetEncodedPathAndQuery();
|
||||
|
||||
try
|
||||
{
|
||||
if (isFrontEndRequest)
|
||||
{
|
||||
LogHttpRequest.TryGetCurrentHttpRequestId(out Guid httpRequestId, _requestCache);
|
||||
_logger.LogTrace("Begin request [{HttpRequestId}]: {RequestUrl}", httpRequestId, requestUri);
|
||||
_logger.LogTrace("Begin request [{HttpRequestId}]: {RequestUrl}", httpRequestId, pathAndQuery);
|
||||
}
|
||||
|
||||
try
|
||||
@@ -109,12 +110,12 @@ namespace Umbraco.Web.Common.Middleware
|
||||
if (isFrontEndRequest)
|
||||
{
|
||||
LogHttpRequest.TryGetCurrentHttpRequestId(out var httpRequestId, _requestCache);
|
||||
_logger.LogTrace("End Request [{HttpRequestId}]: {RequestUrl} ({RequestDuration}ms)", httpRequestId, requestUri, DateTime.Now.Subtract(umbracoContextReference.UmbracoContext.ObjectCreated).TotalMilliseconds);
|
||||
_logger.LogTrace("End Request [{HttpRequestId}]: {RequestUrl} ({RequestDuration}ms)", httpRequestId, pathAndQuery, DateTime.Now.Subtract(umbracoContextReference.UmbracoContext.ObjectCreated).TotalMilliseconds);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
DisposeRequestCacheItems(_logger, _requestCache, requestUri);
|
||||
DisposeRequestCacheItems(_logger, _requestCache, context.Request);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -126,10 +127,10 @@ namespace Umbraco.Web.Common.Middleware
|
||||
/// <summary>
|
||||
/// Any object that is in the HttpContext.Items collection that is IDisposable will get disposed on the end of the request
|
||||
/// </summary>
|
||||
private static void DisposeRequestCacheItems(ILogger<UmbracoRequestMiddleware> logger, IRequestCache requestCache, Uri requestUri)
|
||||
private static void DisposeRequestCacheItems(ILogger<UmbracoRequestMiddleware> logger, IRequestCache requestCache, HttpRequest request)
|
||||
{
|
||||
// do not process if client-side request
|
||||
if (requestUri.IsClientSideRequest())
|
||||
if (request.IsClientSideRequest())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -143,6 +144,7 @@ namespace Umbraco.Web.Common.Middleware
|
||||
keys.Add(i.Key);
|
||||
}
|
||||
}
|
||||
|
||||
// dispose each item and key that was found as disposable.
|
||||
foreach (var k in keys)
|
||||
{
|
||||
@@ -154,6 +156,7 @@ namespace Umbraco.Web.Common.Middleware
|
||||
{
|
||||
logger.LogError("Could not dispose item with key " + k, ex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
k.DisposeIfDisposable();
|
||||
|
||||
198
src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs
Normal file
198
src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Routing.Template;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Hosting;
|
||||
|
||||
namespace Umbraco.Web.Common.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class used to check if the current request is for a front-end request
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There are various checks to determine if this is a front-end request such as checking if the request is part of any reserved paths or existing MVC routes.
|
||||
/// </remarks>
|
||||
public sealed class RoutableDocumentFilter
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, bool> _routeChecks = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly WebRoutingSettings _routingSettings;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly EndpointDataSource _endpointDataSource;
|
||||
private readonly object _routeLocker = new object();
|
||||
private object _initLocker = new object();
|
||||
private bool _isInit = false;
|
||||
private HashSet<string> _reservedList;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutableDocumentFilter"/> class.
|
||||
/// </summary>
|
||||
public RoutableDocumentFilter(IOptions<GlobalSettings> globalSettings, IOptions<WebRoutingSettings> routingSettings, IHostingEnvironment hostingEnvironment, EndpointDataSource endpointDataSource)
|
||||
{
|
||||
_globalSettings = globalSettings.Value;
|
||||
_routingSettings = routingSettings.Value;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_endpointDataSource = endpointDataSource;
|
||||
_endpointDataSource.GetChangeToken().RegisterChangeCallback(EndpointsChanged, null);
|
||||
}
|
||||
|
||||
private void EndpointsChanged(object value)
|
||||
{
|
||||
lock (_routeLocker)
|
||||
{
|
||||
// try clearing each entry
|
||||
foreach (var r in _routeChecks.Keys.ToList())
|
||||
{
|
||||
_routeChecks.TryRemove(r, out _);
|
||||
}
|
||||
|
||||
// re-register after it has changed so we keep listening
|
||||
_endpointDataSource.GetChangeToken().RegisterChangeCallback(EndpointsChanged, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the request is a document request (i.e. one that the module should handle)
|
||||
/// </summary>
|
||||
public bool IsDocumentRequest(string absPath)
|
||||
{
|
||||
var maybeDoc = true;
|
||||
|
||||
// a document request should be
|
||||
// /foo/bar/nil
|
||||
// /foo/bar/nil/
|
||||
// where /foo is not a reserved path
|
||||
|
||||
// if the path contains an extension
|
||||
// then it cannot be a document request
|
||||
var extension = Path.GetExtension(absPath);
|
||||
if (maybeDoc && !extension.IsNullOrWhiteSpace())
|
||||
{
|
||||
maybeDoc = false;
|
||||
}
|
||||
|
||||
// at that point we have no extension
|
||||
|
||||
// if the path is reserved then it cannot be a document request
|
||||
if (maybeDoc && IsReservedPathOrUrl(absPath))
|
||||
{
|
||||
maybeDoc = false;
|
||||
}
|
||||
|
||||
return maybeDoc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified URL is reserved or is inside a reserved path.
|
||||
/// </summary>
|
||||
/// <param name="absPath">The Path of the URL to check.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified URL is reserved; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
private bool IsReservedPathOrUrl(string absPath)
|
||||
{
|
||||
LazyInitializer.EnsureInitialized(ref _reservedList, ref _isInit, ref _initLocker, () =>
|
||||
{
|
||||
// store references to strings to determine changes
|
||||
var reservedPathsCache = _globalSettings.ReservedPaths;
|
||||
var reservedUrlsCache = _globalSettings.ReservedUrls;
|
||||
|
||||
// add URLs and paths to a new list
|
||||
var newReservedList = new HashSet<string>();
|
||||
foreach (var reservedUrlTrimmed in NormalizePaths(reservedUrlsCache, false))
|
||||
{
|
||||
newReservedList.Add(reservedUrlTrimmed);
|
||||
}
|
||||
|
||||
foreach (var reservedPathTrimmed in NormalizePaths(reservedPathsCache, true))
|
||||
{
|
||||
newReservedList.Add(reservedPathTrimmed);
|
||||
}
|
||||
|
||||
// use the new list from now on
|
||||
return newReservedList;
|
||||
});
|
||||
|
||||
// The URL should be cleaned up before checking:
|
||||
// * If it doesn't contain an '.' in the path then we assume it is a path based URL, if that is the case we should add an trailing '/' because all of our reservedPaths use a trailing '/'
|
||||
// * We shouldn't be comparing the query at all
|
||||
if (absPath.Contains('?'))
|
||||
{
|
||||
absPath = absPath.Split('?', StringSplitOptions.RemoveEmptyEntries)[0];
|
||||
}
|
||||
|
||||
if (absPath.Contains('.') == false)
|
||||
{
|
||||
absPath = absPath.EnsureEndsWith('/');
|
||||
}
|
||||
|
||||
// return true if URL starts with an element of the reserved list
|
||||
var isReserved = _reservedList.Any(x => absPath.InvariantStartsWith(x));
|
||||
|
||||
if (isReserved)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If configured, check if the current request matches a route, if so then it is reserved,
|
||||
// else if not configured (default) proceed as normal since we assume the request is for an Umbraco content item.
|
||||
var hasRoute = _routingSettings.TryMatchingEndpointsForAllPages && _routeChecks.GetOrAdd(absPath, x => MatchesEndpoint(absPath));
|
||||
if (hasRoute)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private IEnumerable<string> NormalizePaths(string paths, bool ensureTrailingSlash) => paths
|
||||
.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim().ToLowerInvariant())
|
||||
.Where(x => x.IsNullOrWhiteSpace() == false)
|
||||
.Select(reservedPath =>
|
||||
{
|
||||
var r = _hostingEnvironment.ToAbsolute(reservedPath).Trim().EnsureStartsWith('/');
|
||||
return ensureTrailingSlash
|
||||
? r.EnsureEndsWith('/')
|
||||
: r;
|
||||
})
|
||||
.Where(reservedPathTrimmed => reservedPathTrimmed.IsNullOrWhiteSpace() == false);
|
||||
|
||||
private bool MatchesEndpoint(string absPath)
|
||||
{
|
||||
// Borrowed and modified from https://stackoverflow.com/a/59550580
|
||||
|
||||
// Return a collection of Microsoft.AspNetCore.Http.Endpoint instances.
|
||||
IEnumerable<RouteEndpoint> routeEndpoints = _endpointDataSource?.Endpoints
|
||||
.OfType<RouteEndpoint>()
|
||||
.Where(x =>
|
||||
{
|
||||
// We don't want to include dynamic endpoints in this check since we would have no idea if that
|
||||
// matches since they will probably match everything.
|
||||
bool isDynamic = x.Metadata.OfType<IDynamicEndpointMetadata>().Any(x => x.IsDynamic);
|
||||
return !isDynamic;
|
||||
});
|
||||
|
||||
var routeValues = new RouteValueDictionary();
|
||||
|
||||
// To get the matchedEndpoint of the provide url
|
||||
RouteEndpoint matchedEndpoint = routeEndpoints
|
||||
.Where(e => new TemplateMatcher(
|
||||
TemplateParser.Parse(e.RoutePattern.RawText),
|
||||
new RouteValueDictionary())
|
||||
.TryMatch(absPath, routeValues))
|
||||
.OrderBy(c => c.Order)
|
||||
.FirstOrDefault();
|
||||
|
||||
return matchedEndpoint != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Routing;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
using Umbraco.Web.Routing;
|
||||
@@ -14,7 +15,6 @@ namespace Umbraco.Web
|
||||
/// </summary>
|
||||
public class UmbracoContext : DisposableObjectSlim, IDisposeOnRequestEnd, IUmbracoContext
|
||||
{
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly ICookieManager _cookieManager;
|
||||
private readonly IRequestAccessor _requestAccessor;
|
||||
@@ -22,6 +22,7 @@ namespace Umbraco.Web
|
||||
private string _previewToken;
|
||||
private bool? _previewing;
|
||||
private readonly IBackOfficeSecurity _backofficeSecurity;
|
||||
private readonly UmbracoRequestPaths _umbracoRequestPaths;
|
||||
|
||||
// initializes a new instance of the UmbracoContext class
|
||||
// internal for unit tests
|
||||
@@ -30,7 +31,7 @@ namespace Umbraco.Web
|
||||
internal UmbracoContext(
|
||||
IPublishedSnapshotService publishedSnapshotService,
|
||||
IBackOfficeSecurity backofficeSecurity,
|
||||
GlobalSettings globalSettings,
|
||||
UmbracoRequestPaths umbracoRequestPaths,
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
UriUtility uriUtility,
|
||||
@@ -43,7 +44,6 @@ namespace Umbraco.Web
|
||||
}
|
||||
|
||||
VariationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor));
|
||||
_globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings));
|
||||
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_cookieManager = cookieManager;
|
||||
@@ -52,6 +52,7 @@ namespace Umbraco.Web
|
||||
ObjectCreated = DateTime.Now;
|
||||
UmbracoRequestId = Guid.NewGuid();
|
||||
_backofficeSecurity = backofficeSecurity ?? throw new ArgumentNullException(nameof(backofficeSecurity));
|
||||
_umbracoRequestPaths = umbracoRequestPaths;
|
||||
|
||||
// beware - we cannot expect a current user here, so detecting preview mode must be a lazy thing
|
||||
_publishedSnapshot = new Lazy<IPublishedSnapshot>(() => publishedSnapshotService.CreatePublishedSnapshot(PreviewToken));
|
||||
@@ -140,7 +141,7 @@ namespace Umbraco.Web
|
||||
{
|
||||
Uri requestUrl = _requestAccessor.GetRequestUrl();
|
||||
if (requestUrl != null
|
||||
&& requestUrl.IsBackOfficeRequest(_globalSettings, _hostingEnvironment) == false
|
||||
&& _umbracoRequestPaths.IsBackOfficeRequest(requestUrl.AbsolutePath) == false
|
||||
&& _backofficeSecurity.CurrentUser != null)
|
||||
{
|
||||
var previewToken = _cookieManager.GetCookieValue(Constants.Web.PreviewCookieName); // may be null or empty
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Options;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Routing;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
|
||||
@@ -18,7 +19,7 @@ namespace Umbraco.Web
|
||||
private readonly IVariationContextAccessor _variationContextAccessor;
|
||||
private readonly IDefaultCultureAccessor _defaultCultureAccessor;
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly UmbracoRequestPaths _umbracoRequestPaths;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly ICookieManager _cookieManager;
|
||||
private readonly IRequestAccessor _requestAccessor;
|
||||
@@ -33,7 +34,7 @@ namespace Umbraco.Web
|
||||
IPublishedSnapshotService publishedSnapshotService,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
IDefaultCultureAccessor defaultCultureAccessor,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
UmbracoRequestPaths umbracoRequestPaths,
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
UriUtility uriUtility,
|
||||
ICookieManager cookieManager,
|
||||
@@ -44,7 +45,7 @@ namespace Umbraco.Web
|
||||
_publishedSnapshotService = publishedSnapshotService ?? throw new ArgumentNullException(nameof(publishedSnapshotService));
|
||||
_variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor));
|
||||
_defaultCultureAccessor = defaultCultureAccessor ?? throw new ArgumentNullException(nameof(defaultCultureAccessor));
|
||||
_globalSettings = globalSettings.Value ?? throw new ArgumentNullException(nameof(globalSettings));
|
||||
_umbracoRequestPaths = umbracoRequestPaths ?? throw new ArgumentNullException(nameof(umbracoRequestPaths));
|
||||
_hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
|
||||
_uriUtility = uriUtility ?? throw new ArgumentNullException(nameof(uriUtility));
|
||||
_cookieManager = cookieManager ?? throw new ArgumentNullException(nameof(cookieManager));
|
||||
@@ -75,7 +76,7 @@ namespace Umbraco.Web
|
||||
return new UmbracoContext(
|
||||
_publishedSnapshotService,
|
||||
_backofficeSecurityAccessor.BackOfficeSecurity,
|
||||
_globalSettings,
|
||||
_umbracoRequestPaths,
|
||||
_hostingEnvironment,
|
||||
_variationContextAccessor,
|
||||
_uriUtility,
|
||||
|
||||
@@ -6,6 +6,7 @@ using Umbraco.Core.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Infrastructure.DependencyInjection;
|
||||
using Umbraco.Infrastructure.PublishedCache.DependencyInjection;
|
||||
using Umbraco.Web.Common.Routing;
|
||||
using Umbraco.Web.Website.Collections;
|
||||
using Umbraco.Web.Website.Controllers;
|
||||
using Umbraco.Web.Website.Routing;
|
||||
@@ -40,6 +41,7 @@ namespace Umbraco.Web.Website.DependencyInjection
|
||||
builder.Services.AddSingleton<HijackedRouteEvaluator>();
|
||||
builder.Services.AddSingleton<IUmbracoRouteValuesFactory, UmbracoRouteValuesFactory>();
|
||||
builder.Services.AddSingleton<IUmbracoRenderingDefaults, UmbracoRenderingDefaults>();
|
||||
builder.Services.AddSingleton<RoutableDocumentFilter>();
|
||||
|
||||
builder.AddDistributedCache();
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace Umbraco.Web.Website.Routing
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly IRuntimeState _runtime;
|
||||
private readonly IUmbracoRouteValuesFactory _routeValuesFactory;
|
||||
private readonly RoutableDocumentFilter _routableDocumentFilter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UmbracoRouteValueTransformer"/> class.
|
||||
@@ -45,7 +46,8 @@ namespace Umbraco.Web.Website.Routing
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
IRuntimeState runtime,
|
||||
IUmbracoRouteValuesFactory routeValuesFactory)
|
||||
IUmbracoRouteValuesFactory routeValuesFactory,
|
||||
RoutableDocumentFilter routableDocumentFilter)
|
||||
{
|
||||
_logger = logger;
|
||||
_umbracoContextAccessor = umbracoContextAccessor;
|
||||
@@ -54,6 +56,7 @@ namespace Umbraco.Web.Website.Routing
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_runtime = runtime;
|
||||
_routeValuesFactory = routeValuesFactory;
|
||||
_routableDocumentFilter = routableDocumentFilter;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -71,10 +74,7 @@ namespace Umbraco.Web.Website.Routing
|
||||
return values;
|
||||
}
|
||||
|
||||
// Check for back office request
|
||||
// TODO: This is how the module was doing it before but could just as easily be part of the RoutableDocumentFilter
|
||||
// which still needs to be migrated.
|
||||
if (httpContext.Request.IsDefaultBackOfficeRequest(_globalSettings, _hostingEnvironment))
|
||||
if (!_routableDocumentFilter.IsDocumentRequest(httpContext.Request.Path))
|
||||
{
|
||||
return values;
|
||||
}
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Web;
|
||||
using System.Web.Routing;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Web.Composing;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class used to check if the current request is for a front-end request
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There are various checks to determine if this is a front-end request such as checking if the request is part of any reserved paths or existing MVC routes.
|
||||
/// </remarks>
|
||||
public sealed class RoutableDocumentFilter
|
||||
{
|
||||
public RoutableDocumentFilter(GlobalSettings globalSettings, IIOHelper ioHelper)
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
_ioHelper = ioHelper;
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<string, bool> RouteChecks = new ConcurrentDictionary<string, bool>();
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IIOHelper _ioHelper;
|
||||
private object _locker = new object();
|
||||
private bool _isInit = false;
|
||||
private int? _routeCount;
|
||||
private HashSet<string> _reservedList;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the request is a document request (i.e. one that the module should handle)
|
||||
/// </summary>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <param name="uri"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsDocumentRequest(HttpContextBase httpContext, Uri uri)
|
||||
{
|
||||
var maybeDoc = true;
|
||||
var lpath = uri.AbsolutePath.ToLowerInvariant();
|
||||
|
||||
// handle directory-URLs used for asmx
|
||||
// TODO: legacy - what's the point really?
|
||||
var asmxPos = lpath.IndexOf(".asmx/", StringComparison.OrdinalIgnoreCase);
|
||||
if (asmxPos >= 0)
|
||||
{
|
||||
// use uri.AbsolutePath, not path, 'cos path has been lowercased
|
||||
httpContext.RewritePath(uri.AbsolutePath.Substring(0, asmxPos + 5), // filePath
|
||||
uri.AbsolutePath.Substring(asmxPos + 5), // pathInfo
|
||||
uri.Query.TrimStart('?'));
|
||||
maybeDoc = false;
|
||||
}
|
||||
|
||||
// a document request should be
|
||||
// /foo/bar/nil
|
||||
// /foo/bar/nil/
|
||||
// /foo/bar/nil.aspx
|
||||
// where /foo is not a reserved path
|
||||
|
||||
// if the path contains an extension that is not .aspx
|
||||
// then it cannot be a document request
|
||||
var extension = Path.GetExtension(lpath);
|
||||
if (maybeDoc && extension.IsNullOrWhiteSpace() == false && extension != ".aspx")
|
||||
maybeDoc = false;
|
||||
|
||||
// at that point, either we have no extension, or it is .aspx
|
||||
|
||||
// if the path is reserved then it cannot be a document request
|
||||
if (maybeDoc && IsReservedPathOrUrl(lpath, httpContext, RouteTable.Routes))
|
||||
maybeDoc = false;
|
||||
|
||||
//NOTE: No need to warn, plus if we do we should log the document, as this message doesn't really tell us anything :)
|
||||
//if (!maybeDoc)
|
||||
//{
|
||||
// Logger.LogWarning<UmbracoModule>("Not a document");
|
||||
//}
|
||||
return maybeDoc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified URL is reserved or is inside a reserved path.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL to check.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified URL is reserved; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
internal bool IsReservedPathOrUrl(string url)
|
||||
{
|
||||
LazyInitializer.EnsureInitialized(ref _reservedList, ref _isInit, ref _locker, () =>
|
||||
{
|
||||
// store references to strings to determine changes
|
||||
var reservedPathsCache = _globalSettings.ReservedPaths;
|
||||
var reservedUrlsCache = _globalSettings.ReservedUrls;
|
||||
|
||||
// add URLs and paths to a new list
|
||||
var newReservedList = new HashSet<string>();
|
||||
foreach (var reservedUrlTrimmed in reservedUrlsCache
|
||||
.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim().ToLowerInvariant())
|
||||
.Where(x => x.IsNullOrWhiteSpace() == false)
|
||||
.Select(reservedUrl => _ioHelper.ResolveUrl(reservedUrl).Trim().EnsureStartsWith("/"))
|
||||
.Where(reservedUrlTrimmed => reservedUrlTrimmed.IsNullOrWhiteSpace() == false))
|
||||
{
|
||||
newReservedList.Add(reservedUrlTrimmed);
|
||||
}
|
||||
|
||||
foreach (var reservedPathTrimmed in NormalizePaths(reservedPathsCache.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)))
|
||||
{
|
||||
newReservedList.Add(reservedPathTrimmed);
|
||||
}
|
||||
|
||||
foreach (var reservedPathTrimmed in NormalizePaths(ReservedPaths))
|
||||
{
|
||||
newReservedList.Add(reservedPathTrimmed);
|
||||
}
|
||||
|
||||
// use the new list from now on
|
||||
return newReservedList;
|
||||
});
|
||||
|
||||
//The URL should be cleaned up before checking:
|
||||
// * If it doesn't contain an '.' in the path then we assume it is a path based URL, if that is the case we should add an trailing '/' because all of our reservedPaths use a trailing '/'
|
||||
// * We shouldn't be comparing the query at all
|
||||
var pathPart = url.Split(new[] { '?' }, StringSplitOptions.RemoveEmptyEntries)[0].ToLowerInvariant();
|
||||
if (pathPart.Contains(".") == false)
|
||||
{
|
||||
pathPart = pathPart.EnsureEndsWith('/');
|
||||
}
|
||||
|
||||
// return true if URL starts with an element of the reserved list
|
||||
return _reservedList.Any(x => pathPart.InvariantStartsWith(x));
|
||||
}
|
||||
|
||||
private IEnumerable<string> NormalizePaths(IEnumerable<string> paths)
|
||||
{
|
||||
return paths
|
||||
.Select(x => x.Trim().ToLowerInvariant())
|
||||
.Where(x => x.IsNullOrWhiteSpace() == false)
|
||||
.Select(reservedPath => _ioHelper.ResolveUrl(reservedPath).Trim().EnsureStartsWith("/").EnsureEndsWith("/"))
|
||||
.Where(reservedPathTrimmed => reservedPathTrimmed.IsNullOrWhiteSpace() == false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the current request is reserved based on the route table and
|
||||
/// whether the specified URL is reserved or is inside a reserved path.
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <param name="routes">The route collection to lookup the request in</param>
|
||||
/// <returns></returns>
|
||||
internal bool IsReservedPathOrUrl(string url, HttpContextBase httpContext, RouteCollection routes)
|
||||
{
|
||||
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
|
||||
if (routes == null) throw new ArgumentNullException(nameof(routes));
|
||||
|
||||
//This is some rudimentary code to check if the route table has changed at runtime, we're basically just keeping a count
|
||||
//of the routes. This isn't fail safe but there's no way to monitor changes to the route table. Else we need to create a hash
|
||||
//of all routes and then recompare but that will be annoying to do on each request and then we might as well just do the whole MVC
|
||||
//route on each request like we were doing before instead of caching the result of GetRouteData.
|
||||
var changed = false;
|
||||
using (routes.GetReadLock())
|
||||
{
|
||||
if (!_routeCount.HasValue || _routeCount.Value != routes.Count)
|
||||
{
|
||||
//the counts are not set or have changed, need to reset
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
using (routes.GetWriteLock())
|
||||
{
|
||||
_routeCount = routes.Count;
|
||||
|
||||
//try clearing each entry
|
||||
foreach(var r in RouteChecks.Keys.ToList())
|
||||
RouteChecks.TryRemove(r, out _);
|
||||
}
|
||||
}
|
||||
|
||||
var absPath = httpContext?.Request?.Url.AbsolutePath;
|
||||
|
||||
if (absPath.IsNullOrWhiteSpace())
|
||||
return false;
|
||||
|
||||
//check if the current request matches a route, if so then it is reserved.
|
||||
var hasRoute = RouteChecks.GetOrAdd(absPath, x => routes.GetRouteData(httpContext) != null);
|
||||
if (hasRoute)
|
||||
return true;
|
||||
|
||||
//continue with the standard ignore routine
|
||||
return IsReservedPathOrUrl(url);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used internally to track any registered callback paths for Identity providers. If the request path matches
|
||||
/// any of the registered paths, then the module will let the request keep executing
|
||||
/// </summary>
|
||||
internal static readonly ConcurrentHashSet<string> ReservedPaths = new ConcurrentHashSet<string>();
|
||||
}
|
||||
}
|
||||
@@ -46,8 +46,6 @@ namespace Umbraco.Web.Runtime
|
||||
return new UmbracoHelper();
|
||||
});
|
||||
|
||||
builder.Services.AddUnique<RoutableDocumentFilter>();
|
||||
|
||||
// configure the container for web
|
||||
//composition.ConfigureForWeb();
|
||||
|
||||
|
||||
@@ -150,7 +150,6 @@
|
||||
<Compile Include="AspNet\AspNetHttpContextAccessor.cs" />
|
||||
<Compile Include="AspNet\AspNetIpResolver.cs" />
|
||||
<Compile Include="AspNet\AspNetPasswordHasher.cs" />
|
||||
<Compile Include="RoutableDocumentFilter.cs" />
|
||||
<Compile Include="Runtime\AspNetUmbracoBootPermissionChecker.cs" />
|
||||
<Compile Include="Security\MembershipProviderBase.cs" />
|
||||
<Compile Include="Security\MembershipProviderExtensions.cs" />
|
||||
|
||||
@@ -12,18 +12,12 @@ using Umbraco.Web.Security;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that encapsulates Umbraco information of a specific HTTP request
|
||||
/// </summary>
|
||||
// NOTE: has all been ported to netcore but exists here just to keep the build working for tests
|
||||
|
||||
public class UmbracoContext : DisposableObjectSlim, IDisposeOnRequestEnd, IUmbracoContext
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly ICookieManager _cookieManager;
|
||||
private readonly Lazy<IPublishedSnapshot> _publishedSnapshot;
|
||||
private string _previewToken;
|
||||
private bool? _previewing;
|
||||
|
||||
// initializes a new instance of the UmbracoContext class
|
||||
// internal for unit tests
|
||||
@@ -44,9 +38,6 @@ namespace Umbraco.Web
|
||||
if (backofficeSecurity == null) throw new ArgumentNullException(nameof(backofficeSecurity));
|
||||
VariationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor));
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings));
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_cookieManager = cookieManager;
|
||||
|
||||
// ensure that this instance is disposed when the request terminates, though we *also* ensure
|
||||
// this happens in the Umbraco module since the UmbracoCOntext is added to the HttpContext items.
|
||||
@@ -134,68 +125,17 @@ namespace Umbraco.Web
|
||||
/// </summary>
|
||||
public IVariationContextAccessor VariationContextAccessor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the request has debugging enabled
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is debug; otherwise, <c>false</c>.</value>
|
||||
public bool IsDebug
|
||||
{
|
||||
get
|
||||
{
|
||||
var request = GetRequestFromContext();
|
||||
//NOTE: the request can be null during app startup!
|
||||
return Current.HostingEnvironment.IsDebugMode
|
||||
&& request != null
|
||||
&& (string.IsNullOrEmpty(request["umbdebugshowtrace"]) == false
|
||||
|| string.IsNullOrEmpty(request["umbdebug"]) == false
|
||||
|| string.IsNullOrEmpty(request.Cookies["UMB-DEBUG"]?.Value) == false);
|
||||
}
|
||||
}
|
||||
// NOTE: has been ported to netcore
|
||||
public bool IsDebug => false;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the current user is in a preview mode and browsing the site (ie. not in the admin UI)
|
||||
/// </summary>
|
||||
public bool InPreviewMode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_previewing.HasValue == false) DetectPreviewMode();
|
||||
return _previewing ?? false;
|
||||
}
|
||||
private set => _previewing = value;
|
||||
}
|
||||
// NOTE: has been ported to netcore
|
||||
public bool InPreviewMode => false;
|
||||
|
||||
public string PreviewToken
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_previewing.HasValue == false) DetectPreviewMode();
|
||||
return _previewToken;
|
||||
}
|
||||
}
|
||||
// NOTE: has been ported to netcore
|
||||
public string PreviewToken => null;
|
||||
|
||||
private void DetectPreviewMode()
|
||||
{
|
||||
var request = GetRequestFromContext();
|
||||
if (request?.Url != null
|
||||
&& request.Url.IsBackOfficeRequest(_globalSettings, _hostingEnvironment) == false
|
||||
&& Security.CurrentUser != null)
|
||||
{
|
||||
var previewToken = _cookieManager.GetPreviewCookieValue(); // may be null or empty
|
||||
_previewToken = previewToken.IsNullOrWhiteSpace() ? null : previewToken;
|
||||
}
|
||||
|
||||
_previewing = _previewToken.IsNullOrWhiteSpace() == false;
|
||||
}
|
||||
|
||||
// say we render a macro or RTE in a give 'preview' mode that might not be the 'current' one,
|
||||
// then due to the way it all works at the moment, the 'current' published snapshot need to be in the proper
|
||||
// default 'preview' mode - somehow we have to force it. and that could be recursive.
|
||||
public IDisposable ForcedPreview(bool preview)
|
||||
{
|
||||
InPreviewMode = preview;
|
||||
return PublishedSnapshot.ForcedPreview(preview, orig => InPreviewMode = orig);
|
||||
}
|
||||
// NOTE: has been ported to netcore
|
||||
public IDisposable ForcedPreview(bool preview) => null;
|
||||
|
||||
private HttpRequestBase GetRequestFromContext()
|
||||
{
|
||||
@@ -209,17 +149,7 @@ namespace Umbraco.Web
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
// DisposableObject ensures that this runs only once
|
||||
|
||||
Security.DisposeIfDisposable();
|
||||
|
||||
// help caches release resources
|
||||
// (but don't create caches just to dispose them)
|
||||
// context is not multi-threaded
|
||||
if (_publishedSnapshot.IsValueCreated)
|
||||
_publishedSnapshot.Value.Dispose();
|
||||
}
|
||||
// NOTE: has been ported to netcore
|
||||
protected override void DisposeResources() { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Web;
|
||||
using System.Web.Routing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.Models;
|
||||
using Umbraco.Core.Exceptions;
|
||||
@@ -11,7 +10,6 @@ using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Routing;
|
||||
using RouteDirection = Umbraco.Web.Routing.RouteDirection;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
@@ -34,42 +32,29 @@ namespace Umbraco.Web
|
||||
{
|
||||
private readonly IRuntimeState _runtime;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IPublishedRouter _publishedRouter;
|
||||
private readonly IUmbracoContextFactory _umbracoContextFactory;
|
||||
private readonly RoutableDocumentFilter _routableDocumentLookup;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
|
||||
public UmbracoInjectedModule(
|
||||
IRuntimeState runtime,
|
||||
ILogger logger,
|
||||
IPublishedRouter publishedRouter,
|
||||
IUmbracoContextFactory umbracoContextFactory,
|
||||
RoutableDocumentFilter routableDocumentLookup,
|
||||
GlobalSettings globalSettings,
|
||||
IHostingEnvironment hostingEnvironment)
|
||||
{
|
||||
_runtime = runtime;
|
||||
_logger = logger;
|
||||
_publishedRouter = publishedRouter;
|
||||
_umbracoContextFactory = umbracoContextFactory;
|
||||
_routableDocumentLookup = routableDocumentLookup;
|
||||
_globalSettings = globalSettings;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
}
|
||||
|
||||
#region HttpModule event handlers
|
||||
|
||||
/// <summary>
|
||||
/// Begins to process a request.
|
||||
/// </summary>
|
||||
/// <param name="httpContext"></param>
|
||||
private void BeginRequest(HttpContextBase httpContext)
|
||||
{
|
||||
// do not process if client-side request
|
||||
if (httpContext.Request.Url.IsClientSideRequest())
|
||||
return;
|
||||
|
||||
// write the trace output for diagnostics at the end of the request
|
||||
httpContext.Trace.Write("UmbracoModule", "Umbraco request begins");
|
||||
|
||||
@@ -85,69 +70,25 @@ namespace Umbraco.Web
|
||||
/// <summary>
|
||||
/// Processes the Umbraco Request
|
||||
/// </summary>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <remarks>
|
||||
///
|
||||
/// This will check if we are trying to route to the default back office page (i.e. ~/Umbraco/ or ~/Umbraco or ~/Umbraco/Default )
|
||||
/// and ensure that the MVC handler executes for that. This is required because the route for /Umbraco will never execute because
|
||||
/// files/folders exist there and we cannot set the RouteCollection.RouteExistingFiles = true since that will muck a lot of other things up.
|
||||
/// So we handle it here and explicitly execute the MVC controller.
|
||||
///
|
||||
/// </remarks>
|
||||
void ProcessRequest(HttpContextBase httpContext)
|
||||
{
|
||||
// do not process if client-side request
|
||||
if (httpContext.Request.Url.IsClientSideRequest())
|
||||
return;
|
||||
|
||||
if (Current.UmbracoContext == null)
|
||||
throw new InvalidOperationException("The Current.UmbracoContext is null, ProcessRequest cannot proceed unless there is a current UmbracoContext");
|
||||
|
||||
var umbracoContext = Current.UmbracoContext;
|
||||
|
||||
// re-write for the default back office path
|
||||
if (httpContext.Request.Url.IsDefaultBackOfficeRequest(_globalSettings, _hostingEnvironment))
|
||||
{
|
||||
if (EnsureRuntime(httpContext, umbracoContext.OriginalRequestUrl))
|
||||
RewriteToBackOfficeHandler(httpContext);
|
||||
return;
|
||||
}
|
||||
|
||||
// do not process if this request is not a front-end routable page
|
||||
var isRoutableAttempt = EnsureUmbracoRoutablePage(umbracoContext, httpContext);
|
||||
|
||||
// raise event here
|
||||
UmbracoModule.OnRouteAttempt(this, new RoutableAttemptEventArgs(isRoutableAttempt.Result, umbracoContext));
|
||||
if (isRoutableAttempt.Success == false) return;
|
||||
|
||||
httpContext.Trace.Write("UmbracoModule", "Umbraco request confirmed");
|
||||
|
||||
// ok, process
|
||||
|
||||
// note: requestModule.UmbracoRewrite also did some stripping of &umbPage
|
||||
// from the querystring... that was in v3.x to fix some issues with pre-forms
|
||||
// auth. Paul Sterling confirmed in Jan. 2013 that we can get rid of it.
|
||||
|
||||
// instantiate, prepare and process the published content request
|
||||
// important to use CleanedUmbracoUrl - lowercase path-only version of the current URL
|
||||
var requestBuilder = _publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl).Result;
|
||||
var request = umbracoContext.PublishedRequest = _publishedRouter.RouteRequestAsync(requestBuilder, new RouteRequestOptions(RouteDirection.Inbound)).Result;
|
||||
|
||||
// NOTE: This has been ported to netcore
|
||||
// HandleHttpResponseStatus returns a value indicating that the request should
|
||||
// not be processed any further, eg because it has been redirect. then, exit.
|
||||
//if (UmbracoModule.HandleHttpResponseStatus(httpContext, request, _logger))
|
||||
// return;
|
||||
//if (request.HasPublishedContent() == false)
|
||||
// httpContext.RemapHandler(new PublishedContentNotFoundHandler());
|
||||
//else
|
||||
// RewriteToUmbracoHandler(httpContext, request);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Checks the current request and ensures that it is routable based on the structure of the request and URI
|
||||
/// </summary>
|
||||
@@ -157,14 +98,15 @@ namespace Umbraco.Web
|
||||
|
||||
var reason = EnsureRoutableOutcome.IsRoutable;
|
||||
|
||||
// ensure this is a document request
|
||||
if (!_routableDocumentLookup.IsDocumentRequest(httpContext, context.OriginalRequestUrl))
|
||||
{
|
||||
reason = EnsureRoutableOutcome.NotDocumentRequest;
|
||||
}
|
||||
//// ensure this is a document request
|
||||
//if (!_routableDocumentLookup.IsDocumentRequest(httpContext, context.OriginalRequestUrl))
|
||||
//{
|
||||
// reason = EnsureRoutableOutcome.NotDocumentRequest;
|
||||
//}
|
||||
|
||||
// ensure the runtime is in the proper state
|
||||
// and deal with needed redirects, etc
|
||||
else if (!EnsureRuntime(httpContext, uri))
|
||||
if (!EnsureRuntime(httpContext, uri))
|
||||
{
|
||||
reason = EnsureRoutableOutcome.NotReady;
|
||||
}
|
||||
@@ -253,7 +195,6 @@ namespace Umbraco.Web
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region IHttpModule
|
||||
|
||||
|
||||
Reference in New Issue
Block a user