Files
Umbraco-CMS/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs
Nuklon de27eb697e Fixes #13732 - IsBackOfficeRequest is extremely inefficient (#13743)
* Update UmbracoRequestPaths.cs

* Update UmbracoRequestPaths.cs

* remove redundant check

---------

Co-authored-by: Michael <michael@crossingpaths.be>
2023-02-16 23:09:26 +01:00

162 lines
6.0 KiB
C#

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Web.Common.DependencyInjection;
using Umbraco.Extensions;
namespace Umbraco.Cms.Core.Routing;
/// <summary>
/// Utility for checking paths
/// </summary>
public class UmbracoRequestPaths
{
private readonly string _apiMvcPath;
private readonly string _appPath;
private readonly string _backOfficeMvcPath;
private readonly string _backOfficePath;
private readonly string _defaultUmbPath;
private readonly string _defaultUmbPathWithSlash;
private readonly string _installPath;
private readonly string _previewMvcPath;
private readonly string _surfaceMvcPath;
private readonly IOptions<UmbracoRequestPathsOptions> _umbracoRequestPathsOptions;
[Obsolete("Use constructor that takes IOptions<UmbracoRequestPathsOptions> - Will be removed in Umbraco 13")]
public UmbracoRequestPaths(IOptions<GlobalSettings> globalSettings, IHostingEnvironment hostingEnvironment)
: this(globalSettings, hostingEnvironment, StaticServiceProvider.Instance.GetRequiredService<IOptions<UmbracoRequestPathsOptions>>())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoRequestPaths" /> class.
/// </summary>
public UmbracoRequestPaths(IOptions<GlobalSettings> globalSettings, IHostingEnvironment hostingEnvironment, IOptions<UmbracoRequestPathsOptions> umbracoRequestPathsOptions)
{
_appPath = hostingEnvironment.ApplicationVirtualPath;
_backOfficePath = globalSettings.Value.GetBackOfficePath(hostingEnvironment)
.EnsureStartsWith('/').TrimStart(_appPath).EnsureStartsWith('/');
string mvcArea = globalSettings.Value.GetUmbracoMvcArea(hostingEnvironment);
_defaultUmbPath = "/" + mvcArea;
_defaultUmbPathWithSlash = "/" + mvcArea + "/";
_backOfficeMvcPath = "/" + mvcArea + "/BackOffice/";
_previewMvcPath = "/" + mvcArea + "/Preview/";
_surfaceMvcPath = "/" + mvcArea + "/Surface/";
_apiMvcPath = "/" + mvcArea + "/Api/";
_installPath = hostingEnvironment.ToAbsolute(Constants.SystemDirectories.Install);
_umbracoRequestPathsOptions = umbracoRequestPathsOptions;
}
/// <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
/// /Umbraco/Management/Api = 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)
{
string urlPath = absPath.TrimStart(_appPath).EnsureStartsWith('/');
// check if this is in the umbraco back office
if (!urlPath.InvariantStartsWith(_backOfficePath))
{
return false;
}
// if its the normal /umbraco path
if (urlPath.InvariantEquals(_defaultUmbPath) || urlPath.InvariantEquals(_defaultUmbPathWithSlash))
{
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 (_umbracoRequestPathsOptions.Value.IsBackOfficeRequest(urlPath))
{
return true;
}
// if its none of the above, we will have to try to detect if it's a PluginController route
return !IsPluginControllerRoute(urlPath);
}
/// <summary>
/// Checks if the path is from a PluginController route.
/// </summary>
private static bool IsPluginControllerRoute(string path)
{
// 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.
int count = 0;
for (int i = 0; i < path.Length; i++)
{
char chr = path[i];
if (chr == '/')
{
count++;
continue;
}
// Check last char so we can properly determine the number of parts, e.g. /url/path/ has two parts, /url/path/test has three.
if (count == 3)
{
return true;
}
}
return false;
}
/// <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();
}
}