Migrated section controller + LanguageTreeController, ApplciationTreeController.
Updated AngularJsonOnlyConfigurationAttribute to be a TypeFilterAttribute, to allow injeciton into the filter. Note: Left TODO about checking authorization of proxied trees..
This commit is contained in:
@@ -22,7 +22,7 @@ namespace Umbraco.Web.BackOffice.Controllers
|
||||
{
|
||||
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)] // TODO: Maybe this could be applied with our Application Model conventions
|
||||
//[ValidationFilter] // TODO: I don't actually think this is required with our custom Application Model conventions applied
|
||||
[TypeFilter(typeof(AngularJsonOnlyConfigurationAttribute))] // TODO: This could be applied with our Application Model conventions
|
||||
[AngularJsonOnlyConfiguration] // TODO: This could be applied with our Application Model conventions
|
||||
[IsBackOffice] // TODO: This could be applied with our Application Model conventions
|
||||
public class AuthenticationController : UmbracoApiControllerBase
|
||||
{
|
||||
|
||||
@@ -183,14 +183,14 @@ namespace Umbraco.Web.BackOffice.Controllers
|
||||
"imagesApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl<ImagesController>(
|
||||
controller => controller.GetBigThumbnail(""))
|
||||
},
|
||||
//{
|
||||
// "sectionApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<SectionController>(
|
||||
// controller => controller.GetSections())
|
||||
//},
|
||||
//{
|
||||
// "treeApplicationApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ApplicationTreeController>(
|
||||
// controller => controller.GetApplicationTrees(null, null, null, TreeUse.None))
|
||||
//},
|
||||
{
|
||||
"sectionApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl<SectionController>(
|
||||
controller => controller.GetSections())
|
||||
},
|
||||
{
|
||||
"treeApplicationApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl<ApplicationTreeController>(
|
||||
controller => controller.GetApplicationTrees(null, null, null, TreeUse.None))
|
||||
},
|
||||
//{
|
||||
// "contentTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ContentTypeController>(
|
||||
// controller => controller.GetAllowedChildren(0))
|
||||
|
||||
@@ -14,6 +14,7 @@ using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Strings;
|
||||
using Umbraco.Core.Strings.Css;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Stylesheet = Umbraco.Core.Models.Stylesheet;
|
||||
using StylesheetRule = Umbraco.Web.Models.ContentEditing.StylesheetRule;
|
||||
|
||||
@@ -1,62 +1,70 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Web.Mvc;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Strings;
|
||||
using Umbraco.Web.Trees;
|
||||
using Section = Umbraco.Web.Models.ContentEditing.Section;
|
||||
using Umbraco.Web.Models.Trees;
|
||||
using Umbraco.Web.Services;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Umbraco.Core.Mapping;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.BackOffice.Controllers;
|
||||
using Umbraco.Web.Common.Attributes;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Models.Trees;
|
||||
using Umbraco.Web.Security;
|
||||
using Umbraco.Web.Services;
|
||||
using Umbraco.Web.Trees;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// The API controller used for using the list of sections
|
||||
/// The API controller used for using the list of sections
|
||||
/// </summary>
|
||||
[PluginController("UmbracoApi")]
|
||||
public class SectionController : UmbracoAuthorizedJsonController
|
||||
{
|
||||
private readonly IControllerFactory _controllerFactory;
|
||||
private readonly IDashboardService _dashboardService;
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
private readonly ISectionService _sectionService;
|
||||
private readonly ITreeService _treeService;
|
||||
private readonly UmbracoMapper _umbracoMapper;
|
||||
private readonly IWebSecurity _webSecurity;
|
||||
|
||||
public SectionController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState,
|
||||
IDashboardService dashboardService, ISectionService sectionService, ITreeService treeService, IShortStringHelper shortStringHelper, UmbracoMapper umbracoMapper, IPublishedUrlProvider publishedUrlProvider)
|
||||
: base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, shortStringHelper, umbracoMapper, publishedUrlProvider)
|
||||
public SectionController(
|
||||
IWebSecurity webSecurity,
|
||||
ILocalizedTextService localizedTextService,
|
||||
IDashboardService dashboardService, ISectionService sectionService, ITreeService treeService,
|
||||
UmbracoMapper umbracoMapper, IControllerFactory controllerFactory)
|
||||
{
|
||||
_webSecurity = webSecurity;
|
||||
_localizedTextService = localizedTextService;
|
||||
_dashboardService = dashboardService;
|
||||
_sectionService = sectionService;
|
||||
_treeService = treeService;
|
||||
_umbracoMapper = umbracoMapper;
|
||||
_controllerFactory = controllerFactory;
|
||||
}
|
||||
|
||||
public IEnumerable<Section> GetSections()
|
||||
{
|
||||
var sections = _sectionService.GetAllowedSections(Security.GetUserId().ResultOr(0));
|
||||
var sections = _sectionService.GetAllowedSections(_webSecurity.GetUserId().ResultOr(0));
|
||||
|
||||
var sectionModels = sections.Select(Mapper.Map<Section>).ToArray();
|
||||
var sectionModels = sections.Select(_umbracoMapper.Map<Section>).ToArray();
|
||||
|
||||
// this is a bit nasty since we'll be proxying via the app tree controller but we sort of have to do that
|
||||
// since tree's by nature are controllers and require request contextual data
|
||||
var appTreeController = new ApplicationTreeController(GlobalSettings, UmbracoContextAccessor, SqlContext, Services, AppCaches, Logger, RuntimeState, _treeService, _sectionService, Mapper, PublishedUrlProvider)
|
||||
{
|
||||
ControllerContext = ControllerContext
|
||||
};
|
||||
var appTreeController =
|
||||
new ApplicationTreeController(_treeService, _sectionService, _localizedTextService, _controllerFactory)
|
||||
{
|
||||
ControllerContext = ControllerContext
|
||||
};
|
||||
|
||||
var dashboards = _dashboardService.GetDashboards(Security.CurrentUser);
|
||||
var dashboards = _dashboardService.GetDashboards(_webSecurity.CurrentUser);
|
||||
|
||||
//now we can add metadata for each section so that the UI knows if there's actually anything at all to render for
|
||||
//a dashboard for a given section, then the UI can deal with it accordingly (i.e. redirect to the first tree)
|
||||
foreach (var section in sectionModels)
|
||||
{
|
||||
var hasDashboards = dashboards.TryGetValue(section.Alias, out var dashboardsForSection) && dashboardsForSection.Any();
|
||||
var hasDashboards = dashboards.TryGetValue(section.Alias, out var dashboardsForSection) &&
|
||||
dashboardsForSection.Any();
|
||||
if (hasDashboards) continue;
|
||||
|
||||
// get the first tree in the section and get its root node route path
|
||||
@@ -68,7 +76,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the first non root/group node's route path
|
||||
/// Returns the first non root/group node's route path
|
||||
/// </summary>
|
||||
/// <param name="rootNode"></param>
|
||||
/// <returns></returns>
|
||||
@@ -77,30 +85,28 @@ namespace Umbraco.Web.Editors
|
||||
if (!rootNode.IsContainer || !rootNode.ContainsTrees)
|
||||
return rootNode.RoutePath;
|
||||
|
||||
foreach(var node in rootNode.Children)
|
||||
foreach (var node in rootNode.Children)
|
||||
{
|
||||
if (node is TreeRootNode groupRoot)
|
||||
return GetRoutePathForFirstTree(groupRoot);//recurse to get the first tree in the group
|
||||
else
|
||||
return node.RoutePath;
|
||||
return GetRoutePathForFirstTree(groupRoot); //recurse to get the first tree in the group
|
||||
return node.RoutePath;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all the sections that the user has access to
|
||||
/// Returns all the sections that the user has access to
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<Section> GetAllSections()
|
||||
{
|
||||
var sections = _sectionService.GetSections();
|
||||
var mapped = sections.Select(Mapper.Map<Section>);
|
||||
if (Security.CurrentUser.IsAdmin())
|
||||
var mapped = sections.Select(_umbracoMapper.Map<Section>);
|
||||
if (_webSecurity.CurrentUser.IsAdmin())
|
||||
return mapped;
|
||||
|
||||
return mapped.Where(x => Security.CurrentUser.AllowedSections.Contains(x.Alias)).ToArray();
|
||||
return mapped.Where(x => _webSecurity.CurrentUser.AllowedSections.Contains(x.Alias)).ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Web.BackOffice.Controllers
|
||||
/// methods that are not called by Angular or don't contain a valid csrf header will NOT work.
|
||||
/// </remarks>
|
||||
[TypeFilter(typeof(ValidateAngularAntiForgeryTokenAttribute))]
|
||||
[TypeFilter(typeof(AngularJsonOnlyConfigurationAttribute))] // TODO: This could be applied with our Application Model conventions
|
||||
[AngularJsonOnlyConfiguration] // TODO: This could be applied with our Application Model conventions
|
||||
public abstract class UmbracoAuthorizedJsonController : UmbracoAuthorizedApiController
|
||||
{
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Web.Trees;
|
||||
|
||||
// the namespace here is intentional - although defined in Umbraco.Web assembly,
|
||||
// this class should be visible when using Umbraco.Core.Components, alongside
|
||||
// Umbraco.Core's own CompositionExtensions class
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods to the <see cref="Composition"/> class.
|
||||
/// </summary>
|
||||
public static class WebCompositionExtensions
|
||||
{
|
||||
#region Collection Builders
|
||||
|
||||
/// <summary>
|
||||
/// Gets the back office tree collection builder
|
||||
/// </summary>
|
||||
/// <param name="composition"></param>
|
||||
/// <returns></returns>
|
||||
public static TreeCollectionBuilder Trees(this Composition composition)
|
||||
=> composition.WithCollectionBuilder<TreeCollectionBuilder>();
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,34 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.BackOffice;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.BackOffice.Controllers;
|
||||
using Umbraco.Web.BackOffice.Routing;
|
||||
using Umbraco.Web.BackOffice.Security;
|
||||
using Umbraco.Web.BackOffice.Trees;
|
||||
using Umbraco.Web.Common.Runtime;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Runtime
|
||||
{
|
||||
[ComposeBefore(typeof(ICoreComposer))]
|
||||
[ComposeAfter(typeof(AspNetCoreComposer))]
|
||||
public class BackOfficeComposer : IComposer
|
||||
{
|
||||
public void Compose(Composition composition)
|
||||
{
|
||||
|
||||
|
||||
composition.RegisterUnique<BackOfficeAreaRoutes>();
|
||||
composition.RegisterUnique<BackOfficeServerVariables>();
|
||||
|
||||
composition.RegisterUnique<IBackOfficeAntiforgery, BackOfficeAntiforgery>();
|
||||
|
||||
// register back office trees
|
||||
// the collection builder only accepts types inheriting from TreeControllerBase
|
||||
// and will filter out those that are not attributed with TreeAttribute
|
||||
var umbracoApiControllerTypes = composition.TypeLoader.GetUmbracoApiControllers().ToList();
|
||||
composition.Trees()
|
||||
.AddTreeControllers(umbracoApiControllerTypes.Where(x => typeof(TreeControllerBase).IsAssignableFrom(x)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,25 +3,25 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Formatting;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.Controllers;
|
||||
using System.Web.Http.Routing;
|
||||
using System.Web.Mvc;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Mapping;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.BackOffice.Controllers;
|
||||
using Umbraco.Web.BackOffice.Trees;
|
||||
using Umbraco.Web.Common.Attributes;
|
||||
using Umbraco.Web.Common.Exceptions;
|
||||
using Umbraco.Web.Common.Filters;
|
||||
using Umbraco.Web.Common.ModelBinders;
|
||||
using Umbraco.Web.Models.Trees;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Web.Services;
|
||||
using Umbraco.Web.WebApi;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
|
||||
namespace Umbraco.Web.Trees
|
||||
@@ -35,15 +35,21 @@ namespace Umbraco.Web.Trees
|
||||
{
|
||||
private readonly ITreeService _treeService;
|
||||
private readonly ISectionService _sectionService;
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
private readonly IControllerFactory _controllerFactory;
|
||||
|
||||
public ApplicationTreeController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor,
|
||||
ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger,
|
||||
IRuntimeState runtimeState, ITreeService treeService, ISectionService sectionService, UmbracoMapper umbracoMapper, IPublishedUrlProvider publishedUrlProvider)
|
||||
: base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoMapper, publishedUrlProvider)
|
||||
{
|
||||
public ApplicationTreeController(
|
||||
ITreeService treeService,
|
||||
ISectionService sectionService,
|
||||
ILocalizedTextService localizedTextService,
|
||||
IControllerFactory controllerFactory
|
||||
)
|
||||
{
|
||||
_treeService = treeService;
|
||||
_sectionService = sectionService;
|
||||
}
|
||||
_localizedTextService = localizedTextService;
|
||||
_controllerFactory = controllerFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the tree nodes for an application
|
||||
@@ -53,7 +59,7 @@ namespace Umbraco.Web.Trees
|
||||
/// <param name="queryStrings"></param>
|
||||
/// <param name="use">Tree use.</param>
|
||||
/// <returns></returns>
|
||||
public async Task<TreeRootNode> GetApplicationTrees(string application, string tree, [System.Web.Http.ModelBinding.ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings, TreeUse use = TreeUse.Main)
|
||||
public async Task<TreeRootNode> GetApplicationTrees(string application, string tree, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection queryStrings, TreeUse use = TreeUse.Main)
|
||||
{
|
||||
application = application.CleanForXss();
|
||||
|
||||
@@ -72,7 +78,7 @@ namespace Umbraco.Web.Trees
|
||||
{
|
||||
//if there are no trees defined for this section but the section is defined then we can have a simple
|
||||
//full screen section without trees
|
||||
var name = Services.TextService.Localize("sections/" + application);
|
||||
var name = _localizedTextService.Localize("sections/" + application);
|
||||
return TreeRootNode.CreateSingleTreeRoot(Constants.System.RootString, null, null, name, TreeNodeCollection.Empty, true);
|
||||
}
|
||||
|
||||
@@ -105,7 +111,7 @@ namespace Umbraco.Web.Trees
|
||||
nodes.Add(node);
|
||||
}
|
||||
|
||||
var name = Services.TextService.Localize("sections/" + application);
|
||||
var name = _localizedTextService.Localize("sections/" + application);
|
||||
|
||||
if (nodes.Count > 0)
|
||||
{
|
||||
@@ -140,7 +146,7 @@ namespace Umbraco.Web.Trees
|
||||
var name = groupName.IsNullOrWhiteSpace() ? "thirdPartyGroup" : groupName;
|
||||
|
||||
var groupRootNode = TreeRootNode.CreateGroupNode(nodes, application);
|
||||
groupRootNode.Name = Services.TextService.Localize("treeHeaders/" + name);
|
||||
groupRootNode.Name = _localizedTextService.Localize("treeHeaders/" + name);
|
||||
treeRootNodes.Add(groupRootNode);
|
||||
}
|
||||
|
||||
@@ -154,7 +160,7 @@ namespace Umbraco.Web.Trees
|
||||
/// <para>Returns null if the root node could not be obtained due to an HttpResponseException,
|
||||
/// which probably indicates that the user isn't authorized to view that tree.</para>
|
||||
/// </remarks>
|
||||
private async Task<TreeNode> TryGetRootNode(Tree tree, FormDataCollection querystring)
|
||||
private async Task<TreeNode> TryGetRootNode(Tree tree, FormCollection querystring)
|
||||
{
|
||||
if (tree == null) throw new ArgumentNullException(nameof(tree));
|
||||
|
||||
@@ -174,7 +180,7 @@ namespace Umbraco.Web.Trees
|
||||
/// <summary>
|
||||
/// Get the tree root node of a tree.
|
||||
/// </summary>
|
||||
private async Task<TreeRootNode> GetTreeRootNode(Tree tree, int id, FormDataCollection querystring)
|
||||
private async Task<TreeRootNode> GetTreeRootNode(Tree tree, int id, FormCollection querystring)
|
||||
{
|
||||
if (tree == null) throw new ArgumentNullException(nameof(tree));
|
||||
|
||||
@@ -203,7 +209,7 @@ namespace Umbraco.Web.Trees
|
||||
/// <summary>
|
||||
/// Gets the root node of a tree.
|
||||
/// </summary>
|
||||
private async Task<TreeNode> GetRootNode(Tree tree, FormDataCollection querystring)
|
||||
private async Task<TreeNode> GetRootNode(Tree tree, FormCollection querystring)
|
||||
{
|
||||
if (tree == null) throw new ArgumentNullException(nameof(tree));
|
||||
|
||||
@@ -217,16 +223,16 @@ namespace Umbraco.Web.Trees
|
||||
/// <summary>
|
||||
/// Get the child nodes of a tree node.
|
||||
/// </summary>
|
||||
private async Task<TreeNodeCollection> GetChildren(Tree tree, int id, FormDataCollection querystring)
|
||||
private async Task<TreeNodeCollection> GetChildren(Tree tree, int id, FormCollection querystring)
|
||||
{
|
||||
if (tree == null) throw new ArgumentNullException(nameof(tree));
|
||||
|
||||
// the method we proxy has an 'id' parameter which is *not* in the querystring,
|
||||
// we need to add it for the proxy to work (else, it does not find the method,
|
||||
// when trying to run auth filters etc).
|
||||
var d = querystring?.ToDictionary(x => x.Key, x => x.Value) ?? new Dictionary<string, string>();
|
||||
d["id"] = null;
|
||||
var proxyQuerystring = new FormDataCollection(d);
|
||||
var d = querystring?.ToDictionary(x => x.Key, x => x.Value) ?? new Dictionary<string, StringValues>();
|
||||
d["id"] = StringValues.Empty;
|
||||
var proxyQuerystring = new FormCollection(d);
|
||||
|
||||
var controller = (TreeController) await GetApiControllerProxy(tree.TreeControllerType, "GetNodes", proxyQuerystring);
|
||||
return controller.GetNodes(id.ToInvariantString(), querystring);
|
||||
@@ -244,49 +250,74 @@ namespace Umbraco.Web.Trees
|
||||
/// and context etc. so it can execute the specified <paramref name="action"/>. Runs the authorization
|
||||
/// filters for that action, to ensure that the user has permission to execute it.</para>
|
||||
/// </remarks>
|
||||
private async Task<object> GetApiControllerProxy(Type controllerType, string action, FormDataCollection querystring)
|
||||
private async Task<object> GetApiControllerProxy(Type controllerType, string action, FormCollection querystring)
|
||||
{
|
||||
// note: this is all required in order to execute the auth-filters for the sub request, we
|
||||
// need to "trick" web-api into thinking that it is actually executing the proxied controller.
|
||||
|
||||
var context = ControllerContext;
|
||||
|
||||
// get the controller
|
||||
var controller = (ApiController) DependencyResolver.Current.GetService(controllerType)
|
||||
?? throw new Exception($"Failed to create controller of type {controllerType.FullName}.");
|
||||
|
||||
// create the proxy URL for the controller action
|
||||
var proxyUrl = context.Request.RequestUri.GetLeftPart(UriPartial.Authority)
|
||||
+ context.Request.GetUrlHelper().GetUmbracoApiService(action, controllerType)
|
||||
+ "?" + querystring.ToQueryString();
|
||||
|
||||
// create proxy route data specifying the action & controller to execute
|
||||
var proxyRoute = new HttpRouteData(
|
||||
context.RouteData.Route,
|
||||
new HttpRouteValueDictionary(new { action, controller = ControllerExtensions.GetControllerName(controllerType) }));
|
||||
|
||||
// create a proxy request
|
||||
var proxyRequest = new HttpRequestMessage(HttpMethod.Get, proxyUrl);
|
||||
|
||||
// create a proxy controller context
|
||||
var proxyContext = new HttpControllerContext(context.Configuration, proxyRoute, proxyRequest)
|
||||
var routeData = new RouteData(new RouteValueDictionary()
|
||||
{
|
||||
ControllerDescriptor = new HttpControllerDescriptor(context.ControllerDescriptor.Configuration, ControllerExtensions.GetControllerName(controllerType), controllerType),
|
||||
RequestContext = context.RequestContext,
|
||||
Controller = controller
|
||||
};
|
||||
["action"] = action,
|
||||
["controller"] = controllerType.Name.Substring(0,controllerType.Name.Length-10) // remove controller part of name;
|
||||
|
||||
// wire everything
|
||||
controller.ControllerContext = proxyContext;
|
||||
controller.Request = proxyContext.Request;
|
||||
controller.RequestContext.RouteData = proxyRoute;
|
||||
});
|
||||
|
||||
// auth
|
||||
var authResult = await controller.ControllerContext.InvokeAuthorizationFiltersForRequest();
|
||||
if (authResult != null)
|
||||
throw new HttpResponseException(authResult);
|
||||
|
||||
var controllerContext = new ControllerContext(
|
||||
new ActionContext(
|
||||
HttpContext,
|
||||
routeData,
|
||||
new ControllerActionDescriptor()
|
||||
{
|
||||
ControllerTypeInfo = controllerType.GetTypeInfo()
|
||||
}
|
||||
));
|
||||
|
||||
var controller = (TreeController) _controllerFactory.CreateController(controllerContext);
|
||||
|
||||
|
||||
//TODO Refactor trees or reimplement this hacks to check authentication.
|
||||
//https://dev.azure.com/umbraco/D-Team%20Tracker/_workitems/edit/3694
|
||||
|
||||
// var context = ControllerContext;
|
||||
//
|
||||
// // get the controller
|
||||
// var controller = (TreeController) DependencyResolver.Current.GetService(controllerType)
|
||||
// ?? throw new Exception($"Failed to create controller of type {controllerType.FullName}.");
|
||||
//
|
||||
// // create the proxy URL for the controller action
|
||||
// var proxyUrl = HttpContext.Request.RequestUri.GetLeftPart(UriPartial.Authority)
|
||||
// + HttpContext.Request.GetUrlHelper().GetUmbracoApiService(action, controllerType)
|
||||
// + "?" + querystring.ToQueryString();
|
||||
//
|
||||
//
|
||||
//
|
||||
// // create a proxy request
|
||||
// var proxyRequest = new HttpRequestMessage(HttpMethod.Get, proxyUrl);
|
||||
//
|
||||
// // create a proxy controller context
|
||||
// var proxyContext = new HttpControllerContext(context.Configuration, proxyRoute, proxyRequest)
|
||||
// {
|
||||
// ControllerDescriptor = new HttpControllerDescriptor(context.ControllerDescriptor.Configuration, ControllerExtensions.GetControllerName(controllerType), controllerType),
|
||||
// RequestContext = context.RequestContext,
|
||||
// Controller = controller
|
||||
// };
|
||||
//
|
||||
// // wire everything
|
||||
// controller.ControllerContext = proxyContext;
|
||||
// controller.Request = proxyContext.Request;
|
||||
// controller.RequestContext.RouteData = proxyRoute;
|
||||
//
|
||||
// // auth
|
||||
// var authResult = await controller.ControllerContext.InvokeAuthorizationFiltersForRequest();
|
||||
// if (authResult != null)
|
||||
// throw new HttpResponseException(authResult);
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Net.Http.Formatting;
|
||||
using Umbraco.Core;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.BackOffice.Filters;
|
||||
using Umbraco.Web.BackOffice.Trees;
|
||||
using Umbraco.Web.Common.Attributes;
|
||||
using Umbraco.Web.Models.Trees;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using Umbraco.Web.WebApi;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
|
||||
namespace Umbraco.Web.Trees
|
||||
@@ -13,13 +15,20 @@ namespace Umbraco.Web.Trees
|
||||
[CoreTree]
|
||||
public class LanguageTreeController : TreeController
|
||||
{
|
||||
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
|
||||
|
||||
public LanguageTreeController(
|
||||
ILocalizedTextService textService,
|
||||
UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection)
|
||||
: base(textService, umbracoApiControllerTypeCollection)
|
||||
{
|
||||
}
|
||||
protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings)
|
||||
{
|
||||
//We don't have any child nodes & only use the root node to load a custom UI
|
||||
return new TreeNodeCollection();
|
||||
}
|
||||
|
||||
protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings)
|
||||
protected override MenuItemCollection GetMenuForNode(string id, FormCollection queryStrings)
|
||||
{
|
||||
//We don't have any menu item options (such as create/delete/reload) & only use the root node to load a custom UI
|
||||
return null;
|
||||
@@ -29,7 +38,7 @@ namespace Umbraco.Web.Trees
|
||||
/// Helper method to create a root model for a tree
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override TreeNode CreateRootNode(FormDataCollection queryStrings)
|
||||
protected override TreeNode CreateRootNode(FormCollection queryStrings)
|
||||
{
|
||||
var root = base.CreateRootNode(queryStrings);
|
||||
|
||||
@@ -41,5 +50,7 @@ namespace Umbraco.Web.Trees
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Web.BackOffice.Trees;
|
||||
|
||||
namespace Umbraco.Web.Trees
|
||||
{
|
||||
@@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Trees;
|
||||
using Umbraco.Web.WebApi;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Trees
|
||||
{
|
||||
@@ -18,14 +19,8 @@ namespace Umbraco.Web.BackOffice.Trees
|
||||
|
||||
private readonly ILocalizedTextService _textService;
|
||||
|
||||
protected TreeController()
|
||||
{
|
||||
var serviceProvider = HttpContext.RequestServices;
|
||||
_textService = serviceProvider.GetService<ILocalizedTextService>();
|
||||
_treeAttribute = GetTreeAttribute();
|
||||
}
|
||||
|
||||
protected TreeController(ILocalizedTextService textService)
|
||||
protected TreeController(ILocalizedTextService textService, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection)
|
||||
: base(umbracoApiControllerTypeCollection)
|
||||
{
|
||||
_textService = textService ?? throw new ArgumentNullException(nameof(textService));
|
||||
_treeAttribute = GetTreeAttribute();
|
||||
|
||||
@@ -7,6 +7,7 @@ using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.BackOffice.Controllers;
|
||||
using Umbraco.Web.Common.Filters;
|
||||
using Umbraco.Web.Common.ModelBinders;
|
||||
@@ -22,15 +23,16 @@ namespace Umbraco.Web.BackOffice.Trees
|
||||
/// <remarks>
|
||||
/// Developers should generally inherit from TreeController.
|
||||
/// </remarks>
|
||||
[TypeFilter(typeof(AngularJsonOnlyConfigurationAttribute))]
|
||||
[AngularJsonOnlyConfiguration]
|
||||
public abstract class TreeControllerBase : UmbracoAuthorizedApiController, ITree
|
||||
{
|
||||
// TODO: Need to set this, but from where?
|
||||
// Presumably not injecting as this will be a base controller for package/solution developers.
|
||||
private readonly UmbracoApiControllerTypeCollection _apiControllers;
|
||||
|
||||
protected TreeControllerBase()
|
||||
protected TreeControllerBase(UmbracoApiControllerTypeCollection apiControllers)
|
||||
{
|
||||
_apiControllers = apiControllers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -5,11 +5,11 @@ using System.Web;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.BackOffice.Trees;
|
||||
using Umbraco.Web.Common.Extensions;
|
||||
using Umbraco.Web.WebApi;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Trees
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
public static class UrlHelperExtensions
|
||||
{
|
||||
|
||||
@@ -4,9 +4,8 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.Common.Extensions;
|
||||
|
||||
namespace Umbraco.Web.Common.Extensions
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
public static class FormCollectionExtensions
|
||||
{
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
|
||||
using System.Buffers;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Web.Common.Formatters;
|
||||
|
||||
namespace Umbraco.Web.Common.Filters
|
||||
@@ -14,29 +10,40 @@ namespace Umbraco.Web.Common.Filters
|
||||
/// <summary>
|
||||
/// Applying this attribute to any controller will ensure that it only contains one json formatter compatible with the angular json vulnerability prevention.
|
||||
/// </summary>
|
||||
public class AngularJsonOnlyConfigurationAttribute : ActionFilterAttribute
|
||||
public class AngularJsonOnlyConfigurationAttribute : TypeFilterAttribute
|
||||
{
|
||||
private readonly IOptions<MvcNewtonsoftJsonOptions> _mvcNewtonsoftJsonOptions;
|
||||
private readonly ArrayPool<char> _arrayPool;
|
||||
private readonly IOptions<MvcOptions> _options;
|
||||
|
||||
public AngularJsonOnlyConfigurationAttribute(IOptions<MvcNewtonsoftJsonOptions> mvcNewtonsoftJsonOptions, ArrayPool<char> arrayPool, IOptions<MvcOptions> options)
|
||||
public AngularJsonOnlyConfigurationAttribute() : base(typeof(AngularJsonOnlyConfigurationFilter))
|
||||
{
|
||||
_mvcNewtonsoftJsonOptions = mvcNewtonsoftJsonOptions;
|
||||
_arrayPool = arrayPool;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
public override void OnResultExecuting(ResultExecutingContext context)
|
||||
private class AngularJsonOnlyConfigurationFilter : IResultFilter
|
||||
{
|
||||
if (context.Result is ObjectResult objectResult)
|
||||
private readonly IOptions<MvcNewtonsoftJsonOptions> _mvcNewtonsoftJsonOptions;
|
||||
private readonly ArrayPool<char> _arrayPool;
|
||||
private readonly IOptions<MvcOptions> _options;
|
||||
|
||||
public AngularJsonOnlyConfigurationFilter(IOptions<MvcNewtonsoftJsonOptions> mvcNewtonsoftJsonOptions, ArrayPool<char> arrayPool, IOptions<MvcOptions> options)
|
||||
{
|
||||
objectResult.Formatters.Clear();
|
||||
objectResult.Formatters.Add(new AngularJsonMediaTypeFormatter(_mvcNewtonsoftJsonOptions.Value.SerializerSettings, _arrayPool, _options.Value));
|
||||
_mvcNewtonsoftJsonOptions = mvcNewtonsoftJsonOptions;
|
||||
_arrayPool = arrayPool;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
base.OnResultExecuting(context);
|
||||
public void OnResultExecuted(ResultExecutedContext context)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnResultExecuting(ResultExecutingContext context)
|
||||
{
|
||||
if (context.Result is ObjectResult objectResult)
|
||||
{
|
||||
objectResult.Formatters.Clear();
|
||||
objectResult.Formatters.Add(new AngularJsonMediaTypeFormatter(_mvcNewtonsoftJsonOptions.Value.SerializerSettings, _arrayPool, _options.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Umbraco.Web.Common.Install
|
||||
|
||||
[UmbracoApiController]
|
||||
[TypeFilter(typeof(HttpResponseExceptionFilter))]
|
||||
[TypeFilter(typeof(AngularJsonOnlyConfigurationAttribute))]
|
||||
[AngularJsonOnlyConfiguration]
|
||||
[InstallAuthorize]
|
||||
[Area(Umbraco.Core.Constants.Web.Mvc.InstallArea)]
|
||||
public class InstallApiController : ControllerBase
|
||||
@@ -95,7 +95,7 @@ namespace Umbraco.Web.Common.Install
|
||||
|
||||
/// <summary>
|
||||
/// Installs.
|
||||
/// </summary>
|
||||
/// </summary>
|
||||
public async Task<InstallProgressResultModel> PostPerformInstall(InstallInstructions installModel)
|
||||
{
|
||||
if (installModel == null) throw new ArgumentNullException(nameof(installModel));
|
||||
|
||||
@@ -18,10 +18,8 @@ using Umbraco.Web.Common.Install;
|
||||
using Umbraco.Extensions;
|
||||
using System.Linq;
|
||||
using Umbraco.Web.Common.Controllers;
|
||||
using System;
|
||||
using Umbraco.Web.Common.Middleware;
|
||||
using Umbraco.Web.Common.ModelBinding;
|
||||
using Umbraco.Web.Search;
|
||||
using Umbraco.Web.Security;
|
||||
using Umbraco.Web.Trees;
|
||||
|
||||
@@ -83,13 +81,6 @@ namespace Umbraco.Web.Common.Runtime
|
||||
composition.WithCollectionBuilder<UmbracoApiControllerTypeCollectionBuilder>()
|
||||
.Add(umbracoApiControllerTypes);
|
||||
|
||||
// register back office trees
|
||||
// the collection builder only accepts types inheriting from TreeControllerBase
|
||||
// and will filter out those that are not attributed with TreeAttribute
|
||||
// composition.Trees()
|
||||
// .AddTreeControllers(umbracoApiControllerTypes.Where(x => typeof(TreeControllerBase).IsAssignableFrom(x)));
|
||||
composition.RegisterUnique<TreeCollection>(); //TODO replace with collection builder above
|
||||
|
||||
|
||||
composition.RegisterUnique<InstallAreaRoutes>();
|
||||
|
||||
@@ -98,10 +89,6 @@ namespace Umbraco.Web.Common.Runtime
|
||||
composition.RegisterUnique<BootFailedMiddleware>();
|
||||
|
||||
composition.RegisterUnique<UmbracoJsonModelBinder>();
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,16 +41,6 @@ namespace Umbraco.Web
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the back office tree collection builder
|
||||
/// </summary>
|
||||
/// <param name="composition"></param>
|
||||
/// <returns></returns>
|
||||
public static TreeCollectionBuilder Trees(this Composition composition)
|
||||
=> composition.WithCollectionBuilder<TreeCollectionBuilder>();
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Uniques
|
||||
|
||||
@@ -172,14 +172,7 @@ namespace Umbraco.Web.Editors
|
||||
// "imagesApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ImagesController>(
|
||||
// controller => controller.GetBigThumbnail(""))
|
||||
// },
|
||||
{
|
||||
"sectionApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<SectionController>(
|
||||
controller => controller.GetSections())
|
||||
},
|
||||
{
|
||||
"treeApplicationApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ApplicationTreeController>(
|
||||
controller => controller.GetApplicationTrees(null, null, null, TreeUse.None))
|
||||
},
|
||||
|
||||
{
|
||||
"contentTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ContentTypeController>(
|
||||
controller => controller.GetAllowedChildren(0))
|
||||
@@ -196,7 +189,7 @@ namespace Umbraco.Web.Editors
|
||||
"macroApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<MacrosController>(
|
||||
controller => controller.Create(null))
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"currentUserApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<CurrentUserController>(
|
||||
controller => controller.PostChangePassword(null))
|
||||
|
||||
@@ -202,7 +202,6 @@
|
||||
<Compile Include="Security\UserAwarePasswordHasher.cs" />
|
||||
<Compile Include="StringExtensions.cs" />
|
||||
<Compile Include="Trees\ITreeNodeController.cs" />
|
||||
<Compile Include="Trees\TreeCollectionBuilder.cs" />
|
||||
<Compile Include="UmbracoContext.cs" />
|
||||
<Compile Include="UmbracoContextFactory.cs" />
|
||||
<Compile Include="Mvc\UmbracoVirtualNodeByUdiRouteHandler.cs" />
|
||||
@@ -343,7 +342,6 @@
|
||||
<Compile Include="Security\UmbracoBackOfficeCookieAuthOptions.cs" />
|
||||
<Compile Include="Trees\DataTypeTreeController.cs" />
|
||||
<Compile Include="Trees\FileSystemTreeController.cs" />
|
||||
<Compile Include="Trees\LanguageTreeController.cs" />
|
||||
<Compile Include="Trees\MemberTreeController.cs" />
|
||||
<Compile Include="Trees\MenuRenderingEventArgs.cs" />
|
||||
<Compile Include="Trees\TemplatesTreeController.cs" />
|
||||
@@ -374,7 +372,6 @@
|
||||
<Compile Include="Models\RegisterModel.cs" />
|
||||
<Compile Include="Editors\MediaTypeController.cs" />
|
||||
<Compile Include="Security\MembershipHelper.cs" />
|
||||
<Compile Include="Editors\SectionController.cs" />
|
||||
<Compile Include="Editors\UmbracoAuthorizedJsonController.cs" />
|
||||
<Compile Include="HttpCookieExtensions.cs" />
|
||||
<Compile Include="FormDataCollectionExtensions.cs" />
|
||||
@@ -384,7 +381,6 @@
|
||||
<Compile Include="Trees\TreeNodeRenderingEventArgs.cs" />
|
||||
<Compile Include="Trees\TreeNodesRenderingEventArgs.cs" />
|
||||
<Compile Include="Trees\TreeQueryStringParameters.cs" />
|
||||
<Compile Include="Trees\ApplicationTreeController.cs" />
|
||||
<Compile Include="Editors\BackOfficeController.cs" />
|
||||
<Compile Include="Security\Providers\MembersMembershipProvider.cs" />
|
||||
<Compile Include="Security\Providers\MembersRoleProvider.cs" />
|
||||
|
||||
Reference in New Issue
Block a user