Removes UmbracoTreeAuthorizeAttribute and migrates usages to authz policies

This commit is contained in:
Shannon
2020-11-19 19:23:41 +11:00
parent 64609475d3
commit daaade185e
30 changed files with 308 additions and 164 deletions

View File

@@ -0,0 +1,32 @@
namespace Umbraco.Web.BackOffice.Authorization
{
public static class AuthorizationPolicies
{
public const string TreeAccessUsers = "TreeAccessUsers";
public const string TreeAccessPartialViews = "TreeAccessPartialViews";
public const string TreeAccessPartialViewMacros = "TreeAccessPartialViewMacros";
public const string TreeAccessDataTypes = "TreeAccessDataTypes";
public const string TreeAccessPackages = "TreeAccessPackages";
public const string TreeAccessLogs = "TreeAccessLogs";
public const string TreeAccessTemplates = "TreeAccessTemplates";
public const string TreeAccessDictionary = "TreeAccessDictionary";
public const string TreeAccessRelationTypes = "TreeAccessRelationTypes";
public const string TreeAccessMediaTypes = "TreeAccessMediaTypes";
public const string TreeAccessMacros = "TreeAccessMacros";
public const string TreeAccessLanguages = "TreeAccessLanguages";
public const string TreeAccessMemberGroups = "TreeAccessMemberGroups";
public const string TreeAccessDocumentTypes = "TreeAccessDocumentTypes";
public const string TreeAccessMemberTypes = "TreeAccessMemberTypes";
public const string TreeAccessDocumentsOrDocumentTypes = "TreeAccessDocumentsAndDocumentTypes";
public const string TreeAccessMediaOrMediaTypes = "TreeAccessMediaAndMediaTypes";
public const string TreeAccessMembersOrMemberTypes = "TreeAccessMembersAndMemberTypes";
public const string TreeAccessAnySchemaTypes = "TreeAccessSchemaTypes";
public const string TreeAccessDictionaryOrTemplates = "TreeAccessDictionaryOrTemplates";
/// <summary>
/// Defines access based on if the user has access to any tree's exposing any types of content (documents, media, members)
/// or any content types (document types, media types, member types)
/// </summary>
public const string TreeAccessAnyContentOrTypes = "TreeAccessAnyContentOrTypes";
}
}

View File

@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic;
namespace Umbraco.Web.BackOffice.Authorization
{
/// <summary>
/// Authorization requirements for <see cref="UmbracoTreeAuthorizeHandler"/>
/// </summary>
public class TreeAliasesRequirement : IAuthorizationRequirement
{
/// <summary>
/// The aliases for trees that the user will need access to
/// </summary>
public IReadOnlyCollection<string> TreeAliases { get; }
public TreeAliasesRequirement(params string[] aliases) => TreeAliases = aliases;
}
}

View File

@@ -0,0 +1,76 @@
using Microsoft.AspNetCore.Authorization;
using System;
using System.Linq;
using Umbraco.Core;
using System.Threading.Tasks;
using Umbraco.Core.Security;
using Umbraco.Web.Services;
namespace Umbraco.Web.BackOffice.Authorization
{
/// <summary>
/// Ensures that the current user has access to the application for which the specified tree(s) belongs
/// </summary>
/// <remarks>
/// This would allow a tree to be moved between sections.
/// The user only needs access to one of the trees specified, not all of the trees.
/// </remarks>
public class UmbracoTreeAuthorizeHandler : AuthorizationHandler<TreeAliasesRequirement>
{
/// <summary>
/// Can be used by unit tests to enable/disable this filter
/// </summary>
internal static readonly bool Enable = true;
private readonly ITreeService _treeService;
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
/// <summary>
/// Constructor to set authorization to be based on a tree alias for which application security will be applied
/// </summary>
/// <param name="treeService"></param>
/// <param name="backofficeSecurityAccessor"></param>
/// <param name="treeAliases">
/// If the user has access to the application that the treeAlias is specified in, they will be authorized.
/// Multiple trees may be specified.
/// </param>
public UmbracoTreeAuthorizeHandler(ITreeService treeService, IBackOfficeSecurityAccessor backofficeSecurityAccessor)
{
_treeService = treeService ?? throw new ArgumentNullException(nameof(treeService));
_backofficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor));
}
private bool IsAuthorized(TreeAliasesRequirement requirement)
{
if (Enable == false)
{
return true;
}
var apps = requirement.TreeAliases.Select(x => _treeService
.GetByAlias(x))
.WhereNotNull()
.Select(x => x.SectionAlias)
.Distinct()
.ToArray();
return _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser != null
&& apps.Any(app => _backofficeSecurityAccessor.BackOfficeSecurity.UserHasSectionAccess(
app, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser));
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TreeAliasesRequirement requirement)
{
if (IsAuthorized(requirement))
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
}
}

View File

@@ -36,6 +36,8 @@ using ContentType = Umbraco.Core.Models.ContentType;
using Umbraco.Core.Configuration.Models;
using Microsoft.Extensions.Options;
using Umbraco.Core.Serialization;
using Microsoft.AspNetCore.Authorization;
using Umbraco.Web.BackOffice.Authorization;
namespace Umbraco.Web.BackOffice.Controllers
{
@@ -48,7 +50,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// An API controller used for dealing with content types
/// </summary>
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)]
public class ContentTypeController : ContentTypeControllerBase<IContentType>
{
private readonly IEntityXmlSerializer _serializer;
@@ -136,7 +138,7 @@ namespace Umbraco.Web.BackOffice.Controllers
}
[HttpGet]
[UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)]
public bool HasContentNodes(int id)
{
return _contentTypeService.HasContentNodes(id);
@@ -223,10 +225,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// Gets all user defined properties.
/// </summary>
/// <returns></returns>
[UmbracoTreeAuthorize(
Constants.Trees.DocumentTypes, Constants.Trees.Content,
Constants.Trees.MediaTypes, Constants.Trees.Media,
Constants.Trees.MemberTypes, Constants.Trees.Members)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessAnyContentOrTypes)]
public IEnumerable<string> GetAllPropertyTypeAliases()
{
return _contentTypeService.GetAllPropertyTypeAliases();
@@ -236,10 +235,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// Gets all the standard fields.
/// </summary>
/// <returns></returns>
[UmbracoTreeAuthorize(
Constants.Trees.DocumentTypes, Constants.Trees.Content,
Constants.Trees.MediaTypes, Constants.Trees.Media,
Constants.Trees.MemberTypes, Constants.Trees.Members)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessAnyContentOrTypes)]
public IEnumerable<string> GetAllStandardFields()
{
string[] preValuesSource = { "createDate", "creatorName", "level", "nodeType", "nodeTypeAlias", "pageID", "pageName", "parentID", "path", "template", "updateDate", "writerID", "writerName" };
@@ -280,10 +276,7 @@ namespace Umbraco.Web.BackOffice.Controllers
return Ok(result);
}
[UmbracoTreeAuthorize(
Constants.Trees.DocumentTypes, Constants.Trees.Content,
Constants.Trees.MediaTypes, Constants.Trees.Media,
Constants.Trees.MemberTypes, Constants.Trees.Members)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessAnyContentOrTypes)]
public ContentPropertyDisplay GetPropertyTypeScaffold(int id)
{
var dataTypeDiff = _dataTypeService.GetDataType(id);
@@ -523,7 +516,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// Returns the allowed child content type objects for the content item id passed in
/// </summary>
/// <param name="contentId"></param>
[UmbracoTreeAuthorize(Constants.Trees.DocumentTypes, Constants.Trees.Content)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentsOrDocumentTypes)]
public IEnumerable<ContentTypeBasic> GetAllowedChildren(int contentId)
{
if (contentId == Constants.System.RecycleBinContent)

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Net;
using System.Net.Mime;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Umbraco.Core;
@@ -14,6 +15,7 @@ using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Serialization;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Common.Exceptions;
@@ -31,7 +33,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// Content Types, Member Types or Media Types ... and of course to Data Types
/// </remarks>
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[UmbracoTreeAuthorize(Constants.Trees.DataTypes, Constants.Trees.DocumentTypes, Constants.Trees.MediaTypes, Constants.Trees.MemberTypes)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentsOrDocumentTypes)]
public class DataTypeController : BackOfficeNotificationsController
{
private readonly PropertyEditorCollection _propertyEditors;
@@ -414,7 +416,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// <remarks>
/// Permission is granted to this method if the user has access to any of these sections: Content, media, settings, developer, members
/// </remarks>
[UmbracoApplicationAuthorizeAttribute(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
[UmbracoApplicationAuthorize(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
Constants.Applications.Settings, Constants.Applications.Packages)]
public IEnumerable<DataTypeBasic> GetAll()
{
@@ -430,7 +432,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// <remarks>
/// Permission is granted to this method if the user has access to any of these sections: Content, media, settings, developer, members
/// </remarks>
[UmbracoTreeAuthorize(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
[UmbracoApplicationAuthorize(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
Constants.Applications.Settings, Constants.Applications.Packages)]
public IDictionary<string, IEnumerable<DataTypeBasic>> GetGroupedDataTypes()
{
@@ -462,9 +464,8 @@ namespace Umbraco.Web.BackOffice.Controllers
/// <remarks>
/// Permission is granted to this method if the user has access to any of these sections: Content, media, settings, developer, members
/// </remarks>
[UmbracoTreeAuthorize(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
[UmbracoApplicationAuthorize(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
Constants.Applications.Settings, Constants.Applications.Packages)]
public IDictionary<string, IEnumerable<DataTypeBasic>> GetGroupedPropertyEditors()
{
var datatypes = new List<DataTypeBasic>();
@@ -495,9 +496,8 @@ namespace Umbraco.Web.BackOffice.Controllers
/// <remarks>
/// Permission is granted to this method if the user has access to any of these sections: Content, media, settings, developer, members
/// </remarks>
[UmbracoTreeAuthorize(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
[UmbracoApplicationAuthorize(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
Constants.Applications.Settings, Constants.Applications.Packages)]
public IEnumerable<PropertyEditorBasic> GetAllPropertyEditors()
{
return _propertyEditorCollection

View File

@@ -18,6 +18,8 @@ using Umbraco.Web.Security;
using Constants = Umbraco.Core.Constants;
using Umbraco.Core.Configuration.Models;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Authorization;
using Umbraco.Web.BackOffice.Authorization;
namespace Umbraco.Web.BackOffice.Controllers
{
@@ -30,7 +32,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// Dictionary
/// </remarks>
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[UmbracoTreeAuthorize(Constants.Trees.Dictionary)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDictionary)]
public class DictionaryController : BackOfficeNotificationsController
{
private readonly ILogger<DictionaryController> _logger;

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Umbraco.Core;
@@ -10,6 +11,7 @@ using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Mapping;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Common.Exceptions;
@@ -80,7 +82,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// <summary>
/// Deletes a language with a given ID
/// </summary>
[UmbracoTreeAuthorize(Constants.Trees.Languages)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessLanguages)]
[HttpDelete]
[HttpPost]
public IActionResult DeleteLanguage(int id)
@@ -109,7 +111,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// <summary>
/// Creates or saves a language
/// </summary>
[UmbracoTreeAuthorize(Constants.Trees.Languages)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessLanguages)]
[HttpPost]
public Language SaveLanguage(Language language)
{

View File

@@ -19,6 +19,8 @@ using Umbraco.Web.Security;
using Umbraco.Core;
using Umbraco.Core.Mapping;
using Umbraco.Core.Security;
using Umbraco.Web.BackOffice.Authorization;
using Microsoft.AspNetCore.Authorization;
namespace Umbraco.Web.BackOffice.Controllers
{
@@ -27,7 +29,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// The API controller used for editing dictionary items
/// </summary>
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[UmbracoTreeAuthorize(Constants.Trees.Macros)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMacros)]
public class MacrosController : BackOfficeNotificationsController
{
private readonly ParameterEditorCollection _parameterEditorCollection;

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Core;
using Umbraco.Core.Dictionary;
@@ -10,6 +11,7 @@ using Umbraco.Core.Models;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Core.Strings;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Common.Exceptions;
@@ -27,7 +29,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// An API controller used for dealing with content types
/// </summary>
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[UmbracoTreeAuthorize(Constants.Trees.MediaTypes)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)]
public class MediaTypeController : ContentTypeControllerBase<IMediaType>
{
private readonly IContentTypeService _contentTypeService;
@@ -78,7 +80,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// <param name="id"></param>
/// <returns></returns>
[DetermineAmbiguousActionByPassingParameters]
[UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)]
public MediaTypeDisplay GetById(int id)
{
var ct = _mediaTypeService.Get(id);
@@ -97,7 +99,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// <param name="id"></param>
/// <returns></returns>
[DetermineAmbiguousActionByPassingParameters]
[UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)]
public MediaTypeDisplay GetById(Guid id)
{
var mediaType = _mediaTypeService.Get(id);
@@ -116,7 +118,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// <param name="id"></param>
/// <returns></returns>
[DetermineAmbiguousActionByPassingParameters]
[UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)]
public MediaTypeDisplay GetById(Udi id)
{
var guidUdi = id as GuidUdi;
@@ -314,7 +316,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// Returns the allowed child content type objects for the content item id passed in - based on an INT id
/// </summary>
/// <param name="contentId"></param>
[UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)]
[DetermineAmbiguousActionByPassingParameters]
public IEnumerable<ContentTypeBasic> GetAllowedChildren(int contentId)
{
@@ -361,7 +363,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// Returns the allowed child content type objects for the content item id passed in - based on a GUID id
/// </summary>
/// <param name="contentId"></param>
[UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)]
[DetermineAmbiguousActionByPassingParameters]
public IEnumerable<ContentTypeBasic> GetAllowedChildren(Guid contentId)
{
@@ -378,7 +380,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// Returns the allowed child content type objects for the content item id passed in - based on a UDI id
/// </summary>
/// <param name="contentId"></param>
[UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)]
[DetermineAmbiguousActionByPassingParameters]
public IEnumerable<ContentTypeBasic> GetAllowedChildren(Udi contentId)
{

View File

@@ -2,11 +2,13 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Core;
using Umbraco.Core.Mapping;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Common.Exceptions;
@@ -19,7 +21,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// An API controller used for dealing with member groups
/// </summary>
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[UmbracoTreeAuthorize(Constants.Trees.MemberGroups)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMemberGroups)]
public class MemberGroupController : UmbracoAuthorizedJsonController
{
private readonly IMemberGroupService _memberGroupService;

View File

@@ -24,6 +24,8 @@ using Umbraco.Web.Common.Exceptions;
using Umbraco.Web.Editors;
using Umbraco.Web.Routing;
using Umbraco.Web.Security;
using Microsoft.AspNetCore.Authorization;
using Umbraco.Web.BackOffice.Authorization;
namespace Umbraco.Web.BackOffice.Controllers
{
@@ -31,7 +33,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// An API controller used for dealing with member types
/// </summary>
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[UmbracoTreeAuthorize(new string[] { Constants.Trees.MemberTypes, Constants.Trees.Members})]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMemberTypes)]
public class MemberTypeController : ContentTypeControllerBase<IMemberType>
{
private readonly IMemberTypeService _memberTypeService;
@@ -71,7 +73,6 @@ namespace Umbraco.Web.BackOffice.Controllers
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
[DetermineAmbiguousActionByPassingParameters]
public MemberTypeDisplay GetById(int id)
{
@@ -90,7 +91,6 @@ namespace Umbraco.Web.BackOffice.Controllers
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
[DetermineAmbiguousActionByPassingParameters]
public MemberTypeDisplay GetById(Guid id)
{
@@ -109,7 +109,6 @@ namespace Umbraco.Web.BackOffice.Controllers
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
[DetermineAmbiguousActionByPassingParameters]
public MemberTypeDisplay GetById(Udi id)
{
@@ -134,7 +133,6 @@ namespace Umbraco.Web.BackOffice.Controllers
/// <returns></returns>
[HttpDelete]
[HttpPost]
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
public IActionResult DeleteById(int id)
{
var foundType = _memberTypeService.Get(id);
@@ -161,8 +159,6 @@ namespace Umbraco.Web.BackOffice.Controllers
/// be looked up via the db, they need to be passed in.
/// </param>
/// <returns></returns>
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
public IActionResult GetAvailableCompositeMemberTypes(int contentTypeId,
[FromQuery]string[] filterContentTypes,
[FromQuery]string[] filterPropertyTypes)
@@ -176,7 +172,6 @@ namespace Umbraco.Web.BackOffice.Controllers
return Ok(result);
}
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
public MemberTypeDisplay GetEmpty()
{
var ct = new MemberType(_shortStringHelper, -1);
@@ -190,13 +185,13 @@ namespace Umbraco.Web.BackOffice.Controllers
/// <summary>
/// Returns all member types
/// </summary>
[Authorize(Policy = AuthorizationPolicies.TreeAccessMembersOrMemberTypes)]
public IEnumerable<ContentTypeBasic> GetAllTypes()
{
return _memberTypeService.GetAll()
.Select(_umbracoMapper.Map<IMemberType, ContentTypeBasic>);
}
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
public ActionResult<MemberTypeDisplay> PostSave(MemberTypeSave contentTypeSave)
{
//get the persisted member type

View File

@@ -15,6 +15,8 @@ using Umbraco.Core.Mapping;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Common.Exceptions;
using Umbraco.Web.BackOffice.Authorization;
using Microsoft.AspNetCore.Authorization;
namespace Umbraco.Web.BackOffice.Controllers
{
@@ -22,7 +24,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// The API controller for editing relation types.
/// </summary>
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[UmbracoTreeAuthorize(Constants.Trees.RelationTypes)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessRelationTypes)]
public class RelationTypeController : BackOfficeNotificationsController
{
private readonly ILogger<RelationTypeController> _logger;

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Core;
using Umbraco.Core.IO;
@@ -9,6 +10,7 @@ using Umbraco.Core.Mapping;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Core.Strings;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Common.Exceptions;
@@ -18,7 +20,7 @@ using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.BackOffice.Controllers
{
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
[UmbracoTreeAuthorize(Constants.Trees.Templates)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessTemplates)]
public class TemplateController : BackOfficeNotificationsController
{
private readonly IFileService _fileService;

View File

@@ -1,4 +1,5 @@
using System;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
@@ -9,6 +10,7 @@ using Umbraco.Core.Security;
using Umbraco.Core.Serialization;
using Umbraco.Infrastructure.BackOffice;
using Umbraco.Net;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.BackOffice.Security;
using Umbraco.Web.Common.AspNetCore;
@@ -42,6 +44,8 @@ namespace Umbraco.Extensions
// TODO: Need to add more cookie options, see https://github.com/dotnet/aspnetcore/blob/3.0/src/Identity/Core/src/IdentityServiceCollectionExtensions.cs#L45
services.ConfigureOptions<ConfigureBackOfficeCookieOptions>();
services.AddBackOfficeAuthorizationPolicies();
}
public static void AddUmbracoPreview(this IServiceCollection services)
@@ -104,5 +108,82 @@ namespace Umbraco.Extensions
return new BackOfficeIdentityBuilder(services);
}
private static void AddBackOfficeAuthorizationPolicies(this IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.AddPolicy(AuthorizationPolicies.TreeAccessUsers, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Users)));
options.AddPolicy(AuthorizationPolicies.TreeAccessPartialViews, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.PartialViews)));
options.AddPolicy(AuthorizationPolicies.TreeAccessPartialViewMacros, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.PartialViewMacros)));
options.AddPolicy(AuthorizationPolicies.TreeAccessPackages, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Packages)));
options.AddPolicy(AuthorizationPolicies.TreeAccessLogs, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.LogViewer)));
options.AddPolicy(AuthorizationPolicies.TreeAccessDataTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.DataTypes)));
options.AddPolicy(AuthorizationPolicies.TreeAccessTemplates, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Templates)));
options.AddPolicy(AuthorizationPolicies.TreeAccessMemberTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.MemberTypes)));
options.AddPolicy(AuthorizationPolicies.TreeAccessRelationTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.RelationTypes)));
options.AddPolicy(AuthorizationPolicies.TreeAccessDocumentTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.DocumentTypes)));
options.AddPolicy(AuthorizationPolicies.TreeAccessMemberGroups, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.MemberGroups)));
options.AddPolicy(AuthorizationPolicies.TreeAccessMediaTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.MediaTypes)));
options.AddPolicy(AuthorizationPolicies.TreeAccessMacros, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Macros)));
options.AddPolicy(AuthorizationPolicies.TreeAccessLanguages, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Languages)));
options.AddPolicy(AuthorizationPolicies.TreeAccessDocumentTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Dictionary)));
options.AddPolicy(AuthorizationPolicies.TreeAccessDictionary, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Dictionary, Constants.Trees.Dictionary)));
options.AddPolicy(AuthorizationPolicies.TreeAccessDictionaryOrTemplates, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Dictionary, Constants.Trees.Templates)));
options.AddPolicy(AuthorizationPolicies.TreeAccessDocumentsOrDocumentTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.DocumentTypes, Constants.Trees.Content)));
options.AddPolicy(AuthorizationPolicies.TreeAccessMediaOrMediaTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.MediaTypes, Constants.Trees.Media)));
options.AddPolicy(AuthorizationPolicies.TreeAccessMembersOrMemberTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.MemberTypes, Constants.Trees.Members)));
options.AddPolicy(AuthorizationPolicies.TreeAccessAnySchemaTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.DataTypes, Constants.Trees.DocumentTypes, Constants.Trees.MediaTypes, Constants.Trees.MemberTypes)));
options.AddPolicy(AuthorizationPolicies.TreeAccessAnyContentOrTypes, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(
Constants.Trees.DocumentTypes, Constants.Trees.Content,
Constants.Trees.MediaTypes, Constants.Trees.Media,
Constants.Trees.MemberTypes, Constants.Trees.Members)));
});
services.AddSingleton<IAuthorizationHandler, UmbracoTreeAuthorizeHandler>();
}
}
}

View File

@@ -1,85 +0,0 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Umbraco.Core;
using Umbraco.Core.Security;
using Umbraco.Web.Security;
using Umbraco.Web.Services;
namespace Umbraco.Web.BackOffice.Filters
{
/// <summary>
/// Ensures that the current user has access to the application for which the specified tree(s) belongs
/// </summary>
/// <remarks>
/// This would allow a tree to be moved between sections
/// </remarks>
public class UmbracoTreeAuthorizeAttribute : TypeFilterAttribute
{
public UmbracoTreeAuthorizeAttribute(params string[] treeAliases) : base(typeof(UmbracoTreeAuthorizeFilter))
{
Arguments = new object[]
{
treeAliases
};
}
private sealed class UmbracoTreeAuthorizeFilter : IAuthorizationFilter
{
/// <summary>
/// Can be used by unit tests to enable/disable this filter
/// </summary>
internal static readonly bool Enable = true;
private readonly string[] _treeAliases;
private readonly ITreeService _treeService;
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
/// <summary>
/// Constructor to set authorization to be based on a tree alias for which application security will be applied
/// </summary>
/// <param name="treeService"></param>
/// <param name="backofficeSecurityAccessor"></param>
/// <param name="treeAliases">
/// If the user has access to the application that the treeAlias is specified in, they will be authorized.
/// Multiple trees may be specified.
/// </param>
public UmbracoTreeAuthorizeFilter(ITreeService treeService, IBackOfficeSecurityAccessor backofficeSecurityAccessor,
params string[] treeAliases)
{
_treeService = treeService ?? throw new ArgumentNullException(nameof(treeService));
_backofficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor));
_treeAliases = treeAliases;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
if (!IsAuthorized())
{
context.Result = new ForbidResult();
}
}
private bool IsAuthorized()
{
if (Enable == false)
{
return true;
}
var apps = _treeAliases.Select(x => _treeService
.GetByAlias(x))
.WhereNotNull()
.Select(x => x.SectionAlias)
.Distinct()
.ToArray();
return _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser != null
&& apps.Any(app => _backofficeSecurityAccessor.BackOfficeSecurity.UserHasSectionAccess(
app, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser));
}
}
}
}

View File

@@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Actions;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Models.ContentEditing;
@@ -16,7 +18,7 @@ using Umbraco.Web.WebApi;
namespace Umbraco.Web.BackOffice.Trees
{
[UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)]
[Tree(Constants.Applications.Settings, Constants.Trees.DocumentTypes, SortOrder = 0, TreeGroup = Constants.Trees.Groups.Settings)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]

View File

@@ -14,10 +14,12 @@ using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
using Umbraco.Web.BackOffice.Authorization;
using Microsoft.AspNetCore.Authorization;
namespace Umbraco.Web.BackOffice.Trees
{
[UmbracoTreeAuthorize(Constants.Trees.DataTypes)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDataTypes)]
[Tree(Constants.Applications.Settings, Constants.Trees.DataTypes, SortOrder = 3, TreeGroup = Constants.Trees.Groups.Settings)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]

View File

@@ -1,10 +1,12 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Actions;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Models.Trees;
@@ -14,12 +16,9 @@ using Umbraco.Web.WebApi;
namespace Umbraco.Web.BackOffice.Trees
{
[UmbracoTreeAuthorize(
Constants.Trees.Dictionary,
Constants.Trees.Templates
// We are allowed to see the dictionary tree, if we are allowed to manage templates, such that se can use the
// dictionary items in templates, even when we dont have authorization to manage the dictionary items
)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDictionaryOrTemplates)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]
[Tree(Constants.Applications.Translation, Constants.Trees.Dictionary, TreeGroup = Constants.Trees.Groups.Settings)]

View File

@@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Models.Trees;
@@ -9,7 +11,7 @@ using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.BackOffice.Trees
{
[UmbracoTreeAuthorize(Constants.Trees.Languages)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessLanguages)]
[Tree(Constants.Applications.Settings, Constants.Trees.Languages, SortOrder = 11, TreeGroup = Constants.Trees.Groups.Settings)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]

View File

@@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Models.Trees;
@@ -9,7 +11,7 @@ using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.BackOffice.Trees
{
[UmbracoTreeAuthorize(Constants.Trees.LogViewer)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessLogs)]
[Tree(Constants.Applications.Settings, Constants.Trees.LogViewer, SortOrder= 9, TreeGroup = Constants.Trees.Groups.Settings)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]

View File

@@ -8,10 +8,12 @@ using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
using Constants = Umbraco.Core.Constants;
using Umbraco.Web.BackOffice.Authorization;
using Microsoft.AspNetCore.Authorization;
namespace Umbraco.Web.BackOffice.Trees
{
[UmbracoTreeAuthorize(Constants.Trees.Macros)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMacros)]
[Tree(Constants.Applications.Settings, Constants.Trees.Macros, TreeTitle = "Macros", SortOrder = 4, TreeGroup = Constants.Trees.Groups.Settings)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]

View File

@@ -13,10 +13,12 @@ using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
using Umbraco.Web.BackOffice.Authorization;
using Microsoft.AspNetCore.Authorization;
namespace Umbraco.Web.BackOffice.Trees
{
[UmbracoTreeAuthorize(Constants.Trees.MediaTypes)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)]
[Tree(Constants.Applications.Settings, Constants.Trees.MediaTypes, SortOrder = 1, TreeGroup = Constants.Trees.Groups.Settings)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]

View File

@@ -1,8 +1,10 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Umbraco.Core;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Models.Trees;
@@ -11,7 +13,7 @@ using Umbraco.Web.WebApi;
namespace Umbraco.Web.BackOffice.Trees
{
[UmbracoTreeAuthorize(Constants.Trees.MemberGroups)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMemberGroups)]
[Tree(Constants.Applications.Members, Constants.Trees.MemberGroups, SortOrder = 1)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]

View File

@@ -1,9 +1,11 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.BackOffice.Trees;
using Umbraco.Web.Common.Attributes;
@@ -16,7 +18,7 @@ using Umbraco.Web.WebApi;
namespace Umbraco.Web.BackOffice.Trees
{
[CoreTree]
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMemberTypes)]
[Tree(Constants.Applications.Settings, Constants.Trees.MemberTypes, SortOrder = 2, TreeGroup = Constants.Trees.Groups.Settings)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
public class MemberTypeTreeController : MemberTypeAndGroupTreeControllerBase, ISearchableTree

View File

@@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Models.Trees;
@@ -9,7 +11,7 @@ using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.BackOffice.Trees
{
[UmbracoTreeAuthorize(Constants.Trees.Packages)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessPackages)]
[Tree(Constants.Applications.Packages, Constants.Trees.Packages, SortOrder = 0, IsSingleNodeTree = true)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]

View File

@@ -1,5 +1,7 @@
using Umbraco.Core.IO;
using Microsoft.AspNetCore.Authorization;
using Umbraco.Core.IO;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Trees;
@@ -12,7 +14,7 @@ namespace Umbraco.Web.BackOffice.Trees
/// Tree for displaying partial view macros in the developer app
/// </summary>
[Tree(Constants.Applications.Settings, Constants.Trees.PartialViewMacros, SortOrder = 8, TreeGroup = Constants.Trees.Groups.Templating)]
[UmbracoTreeAuthorize(Constants.Trees.PartialViewMacros)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessPartialViewMacros)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]
public class PartialViewMacrosTreeController : PartialViewsTreeController

View File

@@ -1,5 +1,7 @@
using Umbraco.Core.IO;
using Microsoft.AspNetCore.Authorization;
using Umbraco.Core.IO;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Composing;
@@ -14,7 +16,7 @@ namespace Umbraco.Web.BackOffice.Trees
/// Tree for displaying partial views in the settings app
/// </summary>
[Tree(Core.Constants.Applications.Settings, Core.Constants.Trees.PartialViews, SortOrder = 7, TreeGroup = Core.Constants.Trees.Groups.Templating)]
[UmbracoTreeAuthorize(Constants.Trees.PartialViews)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessPartialViews)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]
public class PartialViewsTreeController : FileSystemTreeController

View File

@@ -9,10 +9,12 @@ using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
using Umbraco.Web.BackOffice.Authorization;
using Microsoft.AspNetCore.Authorization;
namespace Umbraco.Web.BackOffice.Trees
{
[UmbracoTreeAuthorize(Constants.Trees.RelationTypes)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessRelationTypes)]
[Tree(Constants.Applications.Settings, Constants.Trees.RelationTypes, SortOrder = 5, TreeGroup = Constants.Trees.Groups.Settings)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Umbraco.Core;
using Umbraco.Core.Models;
@@ -8,6 +9,7 @@ using Umbraco.Core.Models.Entities;
using Umbraco.Core.Services;
using Umbraco.Extensions;
using Umbraco.Web.Actions;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Models.ContentEditing;
@@ -19,7 +21,7 @@ using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.BackOffice.Trees
{
[UmbracoTreeAuthorize(Constants.Trees.Templates)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessTemplates)]
[Tree(Constants.Applications.Settings, Constants.Trees.Templates, SortOrder = 6, TreeGroup = Constants.Trees.Groups.Templating)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]

View File

@@ -1,22 +1,16 @@
using Microsoft.AspNetCore.Http;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Mapping;
using Umbraco.Core.Persistence;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Routing;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.BackOffice.Trees
{
[UmbracoTreeAuthorize(Constants.Trees.Users)]
[Authorize(Policy = AuthorizationPolicies.TreeAccessUsers)]
[Tree(Constants.Applications.Users, Constants.Trees.Users, SortOrder = 0, IsSingleNodeTree = true)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]