Fixes: U4-4010 Member and user session gets mixed - fixes how we detect a back office request + unit tests and added new IsBackOffice attribute
This commit is contained in:
@@ -11,24 +11,97 @@ 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="applicationPath">
|
||||
/// The current application path or VirtualPath
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
internal static bool IsBackOfficeRequest(this Uri url)
|
||||
/// <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/RestServices = back office
|
||||
/// /Umbraco/BackOffice = back office
|
||||
/// /Umbraco/UmbracpApi = back office
|
||||
/// /Umbraco/UmbracoTrees = 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>
|
||||
internal static bool IsBackOfficeRequest(this Uri url, string applicationPath)
|
||||
{
|
||||
|
||||
var authority = url.GetLeftPart(UriPartial.Authority);
|
||||
var afterAuthority = url.GetLeftPart(UriPartial.Query)
|
||||
.TrimStart(authority)
|
||||
.TrimStart("/");
|
||||
applicationPath = applicationPath ?? string.Empty;
|
||||
|
||||
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
|
||||
return afterAuthority.InvariantStartsWith(GlobalSettings.Path.TrimStart("/"));
|
||||
var isUmbracoPath = urlPath.InvariantStartsWith(GlobalSettings.Path.EnsureStartsWith('/'));
|
||||
//if not, then def not back office
|
||||
if (isUmbracoPath == false) return false;
|
||||
|
||||
//if its the normal /umbraco path
|
||||
if (urlPath.InvariantEquals("/" + GlobalSettings.UmbracoMvcArea)
|
||||
|| urlPath.InvariantEquals("/" + GlobalSettings.UmbracoMvcArea + "/"))
|
||||
{
|
||||
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 extension'd requests
|
||||
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("/" + GlobalSettings.UmbracoMvcArea + "/UmbracoApi/")
|
||||
|| urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/UmbracoTrees/")
|
||||
|| urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/BackOffice/")
|
||||
|| urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/RestServices/"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//check for special front-end paths
|
||||
if (urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/Surface/")
|
||||
|| urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/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>
|
||||
|
||||
@@ -10,20 +10,35 @@ namespace Umbraco.Tests
|
||||
[TestFixture]
|
||||
public class UriExtensionsTests
|
||||
{
|
||||
[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", true)]
|
||||
[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/test/umbraco.aspx", false)]
|
||||
public void Is_Back_Office_Request(string input, bool expected)
|
||||
[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/test/umbraco.aspx", "", false)]
|
||||
[TestCase("http://www.domain.com/Umbraco/restServices/blah", "", true)]
|
||||
[TestCase("http://www.domain.com/Umbraco/Backoffice/blah", "", true)]
|
||||
[TestCase("http://www.domain.com/Umbraco/umbracoapi/blah", "", true)]
|
||||
[TestCase("http://www.domain.com/Umbraco/umbracotrees/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/MyVdir/Umbraco/restServices/blah", "/myvdir", true)]
|
||||
[TestCase("http://www.domain.com/umbraco/webservices/legacyAjaxCalls.asmx/js", "", 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);
|
||||
Assert.AreEqual(expected, source.IsBackOfficeRequest());
|
||||
var source = new Uri(input);
|
||||
Assert.AreEqual(expected, source.IsBackOfficeRequest(virtualPath));
|
||||
}
|
||||
|
||||
[TestCase("http://www.domain.com/install", true)]
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Concurrent;
|
||||
using System.Web.Mvc;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.WebApi;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
@@ -86,14 +87,17 @@ namespace Umbraco.Web.Mvc
|
||||
|
||||
return MetadataStorage.GetOrAdd(type, type1 =>
|
||||
{
|
||||
var attribute = type.GetCustomAttribute<PluginControllerAttribute>(false);
|
||||
var pluginAttribute = type.GetCustomAttribute<PluginControllerAttribute>(false);
|
||||
//check if any inherited class of this type contains the IsBackOffice attribute
|
||||
var backOfficeAttribute = type.GetCustomAttribute<IsBackOfficeAttribute>(true);
|
||||
|
||||
var meta = new PluginControllerMetadata()
|
||||
{
|
||||
AreaName = attribute == null ? null : attribute.AreaName,
|
||||
AreaName = pluginAttribute == null ? null : pluginAttribute.AreaName,
|
||||
ControllerName = ControllerExtensions.GetControllerName(type),
|
||||
ControllerNamespace = type.Namespace,
|
||||
ControllerType = type
|
||||
ControllerType = type,
|
||||
IsBackOffice = backOfficeAttribute != null
|
||||
};
|
||||
|
||||
MetadataStorage.TryAdd(type, meta);
|
||||
|
||||
@@ -11,5 +11,11 @@ namespace Umbraco.Web.Mvc
|
||||
internal string ControllerName { get; set; }
|
||||
internal string ControllerNamespace { get; set; }
|
||||
internal string AreaName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is determined by another attribute [IsBackOffice] which slightly modifies the route path
|
||||
/// allowing us to determine if it is indeed a back office request or not
|
||||
/// </summary>
|
||||
internal bool IsBackOffice { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ namespace Umbraco.Web.Strategies
|
||||
}
|
||||
|
||||
|
||||
static void UmbracoModuleRouteAttempt(object sender, Routing.RoutableAttemptEventArgs e)
|
||||
static void UmbracoModuleRouteAttempt(object sender, RoutableAttemptEventArgs e)
|
||||
{
|
||||
if (e.HttpContext.Request == null || e.HttpContext.Request.Url == null) return;
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Umbraco.Web.Strategies
|
||||
if (e.Outcome == EnsureRoutableOutcome.NotDocumentRequest)
|
||||
{
|
||||
//check if this is in the umbraco back office
|
||||
if (e.HttpContext.Request.Url.IsBackOfficeRequest())
|
||||
if (e.HttpContext.Request.Url.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath))
|
||||
{
|
||||
//yup it's a back office request!
|
||||
using (var lck = new UpgradeableReadLock(Locker))
|
||||
|
||||
@@ -457,6 +457,7 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\Trees\TranslateTreeNames.cs" />
|
||||
<Compile Include="UrlHelperExtensions.cs" />
|
||||
<Compile Include="UrlHelperRenderExtensions.cs" />
|
||||
<Compile Include="WebApi\IsBackOfficeAttribute.cs" />
|
||||
<Compile Include="WebApi\MemberAuthorizeAttribute.cs" />
|
||||
<Compile Include="WebApi\UmbracoApiController.cs" />
|
||||
<Compile Include="WebApi\UmbracoApiControllerResolver.cs" />
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace Umbraco.Web
|
||||
if (http.Request.Url.IsClientSideRequest())
|
||||
return;
|
||||
|
||||
if (app.Request.Url.IsBackOfficeRequest() || app.Request.Url.IsInstallerRequest())
|
||||
if (app.Request.Url.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath) || app.Request.Url.IsInstallerRequest())
|
||||
{
|
||||
var ticket = http.GetUmbracoAuthTicket();
|
||||
if (ticket != null)
|
||||
|
||||
13
src/Umbraco.Web/WebApi/IsBackOfficeAttribute.cs
Normal file
13
src/Umbraco.Web/WebApi/IsBackOfficeAttribute.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Web.WebApi
|
||||
{
|
||||
/// <summary>
|
||||
/// When applied to an api controller it will be routed to the /Umbraco/BackOffice prefix route so we can determine if it
|
||||
/// is a back office route or not.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class IsBackOfficeAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using umbraco.BusinessLogic;
|
||||
|
||||
namespace Umbraco.Web.WebApi
|
||||
{
|
||||
[IsBackOffice]
|
||||
[UmbracoAuthorize]
|
||||
public abstract class UmbracoAuthorizedApiController : UmbracoApiController
|
||||
{
|
||||
|
||||
@@ -231,9 +231,15 @@ namespace Umbraco.Web
|
||||
private void RouteLocalApiController(Type controller, string umbracoPath)
|
||||
{
|
||||
var meta = PluginController.GetMetadata(controller);
|
||||
|
||||
//url to match
|
||||
var routePath = meta.IsBackOffice == false
|
||||
? umbracoPath + "/Api/" + meta.ControllerName + "/{action}/{id}"
|
||||
: umbracoPath + "/BackOffice/Api/" + meta.ControllerName + "/{action}/{id}";
|
||||
|
||||
var route = RouteTable.Routes.MapHttpRoute(
|
||||
string.Format("umbraco-{0}-{1}", "api", meta.ControllerName),
|
||||
umbracoPath + "/Api/" + meta.ControllerName + "/{action}/{id}", //url to match
|
||||
routePath,
|
||||
new {controller = meta.ControllerName, id = UrlParameter.Optional});
|
||||
//web api routes don't set the data tokens object
|
||||
if (route.DataTokens == null)
|
||||
|
||||
Reference in New Issue
Block a user