Moves the PreviewController to netcore, ports the UmbracoAuthorizeAttribute to netcore, routes the minimal back office controllers and dynamically routes based on runtime.

This commit is contained in:
Shannon
2020-05-13 16:09:54 +10:00
parent 4d6df1405f
commit 768d95588f
16 changed files with 278 additions and 69 deletions

View File

@@ -7,12 +7,12 @@ using Umbraco.Web.Trees;
namespace Umbraco.Web.Editors
{
// TODO: Almost nothing here needs to exist since we can inject these into the view
public class BackOfficeModel
{
public BackOfficeModel(UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion,
IContentSettings contentSettings, TreeCollection treeCollection,
IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment,
IHostingEnvironment hostingEnvironment,
IRuntimeSettings runtimeSettings, ISecuritySettings securitySettings)
{
Features = features;
@@ -20,7 +20,6 @@ namespace Umbraco.Web.Editors
UmbracoVersion = umbracoVersion;
ContentSettings = contentSettings;
TreeCollection = treeCollection;
HttpContextAccessor = httpContextAccessor;
HostingEnvironment = hostingEnvironment;
RuntimeSettings = runtimeSettings;
SecuritySettings = securitySettings;
@@ -32,7 +31,6 @@ namespace Umbraco.Web.Editors
public IUmbracoVersion UmbracoVersion { get; }
public IContentSettings ContentSettings { get; }
public TreeCollection TreeCollection { get; }
public IHttpContextAccessor HttpContextAccessor { get; }
public IHostingEnvironment HostingEnvironment { get; }
public IRuntimeSettings RuntimeSettings { get; set; }
public ISecuritySettings SecuritySettings { get; set; }

View File

@@ -2,20 +2,20 @@
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Web.Features;
using Umbraco.Web.Trees;
namespace Umbraco.Web.Editors
{
// TODO: Almost nothing here needs to exist since we can inject these into the view
public class BackOfficePreviewModel : BackOfficeModel
{
private readonly UmbracoFeatures _features;
public IEnumerable<ILanguage> Languages { get; }
public BackOfficePreviewModel(UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion, IEnumerable<ILanguage> languages, IContentSettings contentSettings, TreeCollection treeCollection, IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment, IRuntimeSettings runtimeSettings, ISecuritySettings securitySettings)
: base(features, globalSettings, umbracoVersion, contentSettings, treeCollection, httpContextAccessor, hostingEnvironment, runtimeSettings, securitySettings)
public BackOfficePreviewModel(UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion, IEnumerable<ILanguage> languages, IContentSettings contentSettings, TreeCollection treeCollection, IHostingEnvironment hostingEnvironment, IRuntimeSettings runtimeSettings, ISecuritySettings securitySettings)
: base(features, globalSettings, umbracoVersion, contentSettings, treeCollection, hostingEnvironment, runtimeSettings, securitySettings)
{
_features = features;
Languages = languages;

View File

@@ -0,0 +1,23 @@
using Umbraco.Core;
namespace Umbraco.Web
{
public static class CookieManagerExtensions
{
public static string GetPreviewCookieValue(this ICookieManager cookieManager)
{
return cookieManager.GetCookieValue(Constants.Web.PreviewCookieName);
}
/// <summary>
/// Does a preview cookie exist ?
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public static bool HasPreviewCookie(this ICookieManager cookieManager)
{
return cookieManager.HasCookie(Constants.Web.PreviewCookieName);
}
}
}

View File

@@ -1,5 +1,6 @@
namespace Umbraco.Web
{
public interface ICookieManager
{
void ExpireCookie(string cookieName);

View File

@@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Mvc;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Common.Filters;
namespace Umbraco.Web.BackOffice.Controllers
{
[Area(Umbraco.Core.Constants.Web.Mvc.BackOfficeArea)]
//[ValidationFilter] // TODO: I don't actually think this is required with our custom Application Model conventions applied
[TypeFilter(typeof(AngularJsonOnlyConfigurationAttribute))]
[IsBackOffice]
public class AuthenticationController : ControllerBase
{
// TODO: We need to import the logic from Umbraco.Web.Editors.AuthenticationController and it should not be an auto-routed api controller
}
}

View File

@@ -10,6 +10,7 @@ using Umbraco.Web.WebAssets;
namespace Umbraco.Web.BackOffice.Controllers
{
[Area(Umbraco.Core.Constants.Web.Mvc.BackOfficeArea)]
public class BackOfficeController : Controller
{

View File

@@ -1,25 +1,28 @@
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using System;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using System.Web.UI;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Hosting;
using Umbraco.Core.Services;
using Umbraco.Core.WebAssets;
using Umbraco.Web.Composing;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.ActionResults;
using Umbraco.Web.Common.Filters;
using Umbraco.Web.Editors;
using Umbraco.Web.Features;
using Umbraco.Web.Mvc;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Trees;
using Umbraco.Web.WebAssets;
using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.Editors
namespace Umbraco.Web.BackOffice.Controllers
{
[DisableBrowserCache]
[Area(Umbraco.Core.Constants.Web.Mvc.BackOfficeArea)]
public class PreviewController : Controller
{
private readonly UmbracoFeatures _features;
@@ -36,6 +39,7 @@ namespace Umbraco.Web.Editors
private readonly IRuntimeSettings _runtimeSettings;
private readonly ISecuritySettings _securitySettings;
private readonly IRuntimeMinifier _runtimeMinifier;
private readonly ICompositeViewEngine _viewEngines;
public PreviewController(
UmbracoFeatures features,
@@ -51,7 +55,8 @@ namespace Umbraco.Web.Editors
ICookieManager cookieManager,
IRuntimeSettings settings,
ISecuritySettings securitySettings,
IRuntimeMinifier runtimeMinifier)
IRuntimeMinifier runtimeMinifier,
ICompositeViewEngine viewEngines)
{
_features = features;
_globalSettings = globalSettings;
@@ -67,6 +72,7 @@ namespace Umbraco.Web.Editors
_runtimeSettings = settings;
_securitySettings = securitySettings;
_runtimeMinifier = runtimeMinifier;
_viewEngines = viewEngines;
}
[UmbracoAuthorize(redirectToUmbracoLogin: true)]
@@ -75,15 +81,13 @@ namespace Umbraco.Web.Editors
{
var availableLanguages = _localizationService.GetAllLanguages();
var model = new BackOfficePreviewModel(_features, _globalSettings, _umbracoVersion, availableLanguages, _contentSettings, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings);
var model = new BackOfficePreviewModel(_features, _globalSettings, _umbracoVersion, availableLanguages, _contentSettings, _treeCollection, _hostingEnvironment, _runtimeSettings, _securitySettings);
if (model.PreviewExtendedHeaderView.IsNullOrWhiteSpace() == false)
{
var viewEngineResult = ViewEngines.Engines.FindPartialView(ControllerContext, model.PreviewExtendedHeaderView);
var viewEngineResult = _viewEngines.FindView(ControllerContext, model.PreviewExtendedHeaderView, false);
if (viewEngineResult.View == null)
{
throw new InvalidOperationException("Could not find the view " + model.PreviewExtendedHeaderView + ", the following locations were searched: " + Environment.NewLine + string.Join(Environment.NewLine, viewEngineResult.SearchedLocations));
}
}
return View(_globalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/Preview/" + "Index.cshtml", model);
@@ -94,13 +98,14 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <returns></returns>
[MinifyJavaScriptResult(Order = 0)]
[OutputCache(Order = 1, VaryByParam = "none", Location = OutputCacheLocation.Server, Duration = 5000)]
// TODO: Replace this with response caching https://docs.microsoft.com/en-us/aspnet/core/performance/caching/response?view=aspnetcore-3.1
//[OutputCache(Order = 1, VaryByParam = "none", Location = OutputCacheLocation.Server, Duration = 5000)]
public async Task<JavaScriptResult> Application()
{
var files = await _runtimeMinifier.GetAssetPathsAsync(BackOfficeWebAssets.UmbracoPreviewJsBundleName);
var result = BackOfficeJavaScriptInitializer.GetJavascriptInitialization(files, "umbraco.preview", _globalSettings, _hostingEnvironment);
return JavaScript(result);
return new JavaScriptResult(result);
}
/// <summary>
@@ -114,7 +119,7 @@ namespace Umbraco.Web.Editors
var previewToken = _publishedSnapshotService.EnterPreview(user, id);
Response.Cookies.Set(new HttpCookie(Constants.Web.PreviewCookieName, previewToken));
_cookieManager.SetCookieValue(Constants.Web.PreviewCookieName, previewToken);
// use a numeric url because content may not be in cache and so .Url would fail
var query = culture.IsNullOrWhiteSpace() ? string.Empty : $"?culture={culture}";
@@ -126,17 +131,15 @@ namespace Umbraco.Web.Editors
public ActionResult End(string redir = null)
{
var previewToken = _cookieManager.GetPreviewCookieValue();
var service = Current.PublishedSnapshotService;
service.ExitPreview(previewToken);
System.Web.HttpContext.Current.ExpireCookie(Constants.Web.PreviewCookieName);
_publishedSnapshotService.ExitPreview(previewToken);
_cookieManager.ExpireCookie(Constants.Web.PreviewCookieName);
if (Uri.IsWellFormedUriString(redir, UriKind.Relative)
&& redir.StartsWith("//") == false
&& Uri.TryCreate(redir, UriKind.Relative, out Uri url))
{
&& Uri.TryCreate(redir, UriKind.Relative, out var url))
return Redirect(url.ToString());
}
return Redirect("/");
}

View File

@@ -0,0 +1,35 @@
using Microsoft.AspNetCore.Mvc;
namespace Umbraco.Web.BackOffice.Filters
{
/// <summary>
/// Ensures authorization is successful for a back office user.
/// </summary>
public class UmbracoAuthorizeAttribute : TypeFilterAttribute
{
/// <summary>
/// Default constructor
/// </summary>
public UmbracoAuthorizeAttribute() : base(typeof(UmbracoAuthorizeFilter))
{
}
/// <summary>
/// Constructor with redirect umbraco login behavior
/// </summary>
/// <param name="redirectToUmbracoLogin"></param>
public UmbracoAuthorizeAttribute(bool redirectToUmbracoLogin) : base(typeof(UmbracoAuthorizeFilter))
{
Arguments = new object[] { redirectToUmbracoLogin };
}
/// <summary>
/// Constructor with redirect url behavior
/// </summary>
/// <param name="redirectUrl"></param>
public UmbracoAuthorizeAttribute(string redirectUrl) : base(typeof(UmbracoAuthorizeFilter))
{
Arguments = new object[] { redirectUrl };
}
}
}

View File

@@ -0,0 +1,113 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Routing;
using System;
using Umbraco.Core;
using Umbraco.Extensions;
using Umbraco.Web.Security;
namespace Umbraco.Web.BackOffice.Filters
{
/// <summary>
/// Ensures authorization is successful for a back office user.
/// </summary>
public class UmbracoAuthorizeFilter : IAuthorizationFilter
{
private readonly bool _requireApproval;
/// <summary>
/// Can be used by unit tests to enable/disable this filter
/// </summary>
internal static bool Enable = true;
private readonly IUmbracoContextAccessor _umbracoContext;
private readonly IRuntimeState _runtimeState;
private readonly LinkGenerator _linkGenerator;
private readonly bool _redirectToUmbracoLogin;
private string _redirectUrl;
/// <summary>
/// Default constructor
/// </summary>
/// <param name="umbracoContext"></param>
/// <param name="runtimeState"></param>
/// <param name="linkGenerator"></param>
public UmbracoAuthorizeFilter(
IUmbracoContextAccessor umbracoContext, IRuntimeState runtimeState, LinkGenerator linkGenerator)
{
_umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext));
_runtimeState = runtimeState ?? throw new ArgumentNullException(nameof(runtimeState));
_linkGenerator = linkGenerator ?? throw new ArgumentNullException(nameof(linkGenerator));
}
/// <summary>
/// Constructor with redirect umbraco login behavior
/// </summary>
/// <param name="umbracoContext"></param>
/// <param name="runtimeState"></param>
/// <param name="linkGenerator"></param>
/// <param name="redirectToUmbracoLogin">If true will redirect to the umbraco login page if not authorized</param>
public UmbracoAuthorizeFilter(
IUmbracoContextAccessor umbracoContext, IRuntimeState runtimeState, LinkGenerator linkGenerator,
bool redirectToUmbracoLogin)
: this(umbracoContext, runtimeState, linkGenerator)
{
_redirectToUmbracoLogin = redirectToUmbracoLogin;
}
/// <summary>
/// Constructor with redirect url behavior
/// </summary>
/// <param name="umbracoContext"></param>
/// <param name="runtimeState"></param>
/// /// <param name="linkGenerator"></param>
/// <param name="redirectUrl">If specified will redirect to this URL if not authorized</param>
public UmbracoAuthorizeFilter(
IUmbracoContextAccessor umbracoContext, IRuntimeState runtimeState, LinkGenerator linkGenerator,
string redirectUrl)
: this(umbracoContext, runtimeState, linkGenerator)
{
_redirectUrl = redirectUrl;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
if (!IsAuthorized())
{
if (_redirectToUmbracoLogin)
{
_redirectUrl = _linkGenerator.GetBackOfficeUrl();
}
if (!_redirectUrl.IsNullOrWhiteSpace())
{
context.Result = new RedirectResult(_redirectUrl);
}
else
{
context.Result = new ForbidResult();
}
}
}
private bool IsAuthorized()
{
if (Enable == false)
return true;
try
{
// if not configured (install or upgrade) then we can continue
// otherwise we need to ensure that a user is logged in
return _runtimeState.Level == RuntimeLevel.Install
|| _runtimeState.Level == RuntimeLevel.Upgrade
|| _umbracoContext.UmbracoContext?.Security.ValidateCurrentUser(false, _requireApproval) == ValidateRequestAttempt.Success;
}
catch (Exception)
{
return false;
}
}
}
}

View File

@@ -13,27 +13,58 @@ namespace Umbraco.Web.BackOffice.Routing
{
private readonly IGlobalSettings _globalSettings;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IRuntimeState _runtimeState;
private readonly string _umbracoAreaPathSegment;
public BackOfficeAreaRoutes(IGlobalSettings globalSettings, IHostingEnvironment hostingEnvironment)
public BackOfficeAreaRoutes(IGlobalSettings globalSettings, IHostingEnvironment hostingEnvironment, IRuntimeState runtimeState)
{
_globalSettings = globalSettings;
_hostingEnvironment = hostingEnvironment;
_runtimeState = runtimeState;
_umbracoAreaPathSegment = _globalSettings.GetUmbracoMvcArea(_hostingEnvironment);
}
public void CreateRoutes(IEndpointRouteBuilder endpoints)
{
var umbracoPath = _globalSettings.GetUmbracoMvcArea(_hostingEnvironment);
switch (_runtimeState.Level)
{
case RuntimeLevel.Install:
// a new install so we don't route the back office
break;
case RuntimeLevel.Upgrade:
// for upgrades we only need to route the back office and auth controllers
MapMinimalBackOffice(endpoints);
break;
case RuntimeLevel.Run:
// TODO: We need to auto-route "Umbraco Api Controllers" for the back office
MapMinimalBackOffice(endpoints);
// TODO: We will also need to detect runtime state here and redirect to the installer,
// Potentially switch this to dynamic routing so we can essentially disable/overwrite the back office routes to redirect to install
// when required, example https://www.strathweb.com/2019/08/dynamic-controller-routing-in-asp-net-core-3-0/
endpoints.MapAreaControllerRoute(
"Umbraco_preview", // TODO: Same name as before but we should change these so they have a convention
Constants.Web.Mvc.BackOfficeArea,
$"{_umbracoAreaPathSegment}/preview/{{Action}}/{{editor?}}",
new { controller = ControllerExtensions.GetControllerName<PreviewController>(), action = "Index" });
AutoRouteBackOfficeControllers(endpoints);
break;
case RuntimeLevel.BootFailed:
case RuntimeLevel.Unknown:
case RuntimeLevel.Boot:
break;
}
}
/// <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.MapAreaControllerRoute(
"Umbraco_back_office", // TODO: Same name as before but we should change these so they have a convention
Constants.Web.Mvc.BackOfficeArea,
$"{umbracoPath}/{{Action}}/{{id?}}",
$"{_umbracoAreaPathSegment}/{{Action}}/{{id?}}",
new { controller = ControllerExtensions.GetControllerName<BackOfficeController>(), action = "Default" },
// 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...
@@ -44,12 +75,22 @@ namespace Umbraco.Web.BackOffice.Routing
id = @"[a-zA-Z]*"
});
var authControllerName = ControllerExtensions.GetControllerName<AuthenticationController>();
endpoints.MapAreaControllerRoute(
"Umbraco_preview", // TODO: Same name as before but we should change these so they have a convention
Constants.Web.Mvc.BackOfficeArea,
$"{umbracoPath}/preview/{{Action}}/{{editor?}}",
// TODO: Change this to use ControllerExtensions.GetControllerName once the PreviewController is moved to Umbraco.Web.BackOffice.Controllers
new { controller = "Preview", action = "Index" });
$"umbraco-{Constants.Web.Mvc.BackOfficeArea.ToLowerInvariant()}-{authControllerName.ToLowerInvariant()}",
Constants.Web.Mvc.BackOfficeArea,
// TODO: The "BackOffice" path name needs to be a constant - actually all of these routes need to be in a helper so the names are by convention
$"{_umbracoAreaPathSegment}/BackOffice/{authControllerName}/{{Action}}/{{id?}}",
new { controller = authControllerName, action = "Default" });
}
/// <summary>
/// Auto-routes all back office controllers
/// </summary>
private void AutoRouteBackOfficeControllers(IEndpointRouteBuilder endpoints)
{
}
}
}

View File

@@ -104,9 +104,9 @@ namespace Umbraco.Web.Editors
{
return await RenderDefaultOrProcessExternalLoginAsync(
() =>
View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings)),
View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _treeCollection, _hostingEnvironment, _runtimeSettings, _securitySettings)),
() =>
View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings))
View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _treeCollection, _hostingEnvironment, _runtimeSettings, _securitySettings))
);
}
@@ -184,7 +184,7 @@ namespace Umbraco.Web.Editors
{
return await RenderDefaultOrProcessExternalLoginAsync(
//The default view to render when there is no external login info or errors
() => View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/AuthorizeUpgrade.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings)),
() => View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/AuthorizeUpgrade.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _treeCollection, _hostingEnvironment, _runtimeSettings, _securitySettings)),
//The ActionResult to perform if external login is successful
() => Redirect("/"));
}

View File

@@ -97,20 +97,6 @@ namespace Umbraco.Web
return null;
}
public static string GetPreviewCookieValue(this ICookieManager cookieManager)
{
return cookieManager.GetCookieValue(Constants.Web.PreviewCookieName);
}
/// <summary>
/// Does a preview cookie exist ?
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public static bool HasPreviewCookie(this ICookieManager cookieManager)
{
return cookieManager.HasCookie(Constants.Web.PreviewCookieName);
}
/// <summary>
/// Does a preview cookie exist ?
/// </summary>

View File

@@ -7,9 +7,7 @@ using Umbraco.Web.Editors;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// An area registration for back office components
/// </summary>
// TODO: This has been ported to netcore, can be removed
internal class BackOfficeArea : AreaRegistration
{
private readonly IGlobalSettings _globalSettings;

View File

@@ -7,9 +7,8 @@ using Umbraco.Core.Configuration;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Ensures authorization is successful for a back office user.
/// </summary>
// TODO: This has been migrated to netcore and can be removed when ready
public sealed class UmbracoAuthorizeAttribute : AuthorizeAttribute
{
// see note in HttpInstallAuthorizeAttribute

View File

@@ -154,7 +154,6 @@
<Compile Include="WebAssets\CDF\ClientDependencyRuntimeMinifier.cs" />
<Compile Include="Models\NoNodesViewModel.cs" />
<Compile Include="Mvc\RenderNoContentController.cs" />
<Compile Include="Editors\BackOfficePreviewModel.cs" />
<Compile Include="Editors\EditorModelEventManager.cs" />
<Compile Include="Editors\Filters\ContentSaveModelValidator.cs" />
<Compile Include="Editors\Filters\DataTypeValidateAttribute.cs" />
@@ -237,7 +236,6 @@
<Compile Include="CompositionExtensions.cs" />
<Compile Include="Composing\Current.cs" />
<Compile Include="Editors\BackOfficeAssetsController.cs" />
<Compile Include="Editors\BackOfficeModel.cs" />
<Compile Include="Editors\BackOfficeServerVariables.cs" />
<Compile Include="Editors\LogViewerController.cs" />
<Compile Include="ImageProcessorLogger.cs" />
@@ -265,7 +263,6 @@
<Compile Include="Editors\LanguageController.cs" />
<Compile Include="WebApi\ParameterSwapControllerActionSelector.cs" />
<Compile Include="Editors\PasswordChanger.cs" />
<Compile Include="Editors\PreviewController.cs" />
<Compile Include="Editors\TemplateController.cs" />
<Compile Include="Editors\TourController.cs" />
<Compile Include="Editors\Filters\UserGroupAuthorizationAttribute.cs" />

View File

@@ -7,9 +7,8 @@ using Umbraco.Web.Security;
namespace Umbraco.Web.WebApi
{
/// <summary>
/// Ensures authorization is successful for a back office user.
/// </summary>
// TODO: This has been migrated to netcore and can be removed when ready
public sealed class UmbracoAuthorizeAttribute : AuthorizeAttribute
{
private readonly bool _requireApproval;