diff --git a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ModelsBuilderDashboardController.cs b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ModelsBuilderDashboardController.cs
index 3adbc0df2c..76c33e8de4 100644
--- a/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ModelsBuilderDashboardController.cs
+++ b/src/Umbraco.ModelsBuilder.Embedded/BackOffice/ModelsBuilderDashboardController.cs
@@ -1,5 +1,6 @@
using System;
using System.Runtime.Serialization;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Umbraco.Configuration;
@@ -7,6 +8,7 @@ using Umbraco.Core.Configuration.Models;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Hosting;
using Umbraco.ModelsBuilder.Embedded.Building;
+using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Controllers;
using Umbraco.Web.BackOffice.Filters;
@@ -20,7 +22,7 @@ namespace Umbraco.ModelsBuilder.Embedded.BackOffice
/// correct CSRF security is adhered to for angular and it also ensures that this controller is not subseptipal to
/// global WebApi formatters being changed since this is always forced to only return Angular JSON Specific formats.
///
- [UmbracoApplicationAuthorize(Core.Constants.Applications.Settings)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)]
public class ModelsBuilderDashboardController : UmbracoAuthorizedJsonController
{
private readonly ModelsBuilderSettings _config;
diff --git a/src/Umbraco.Web.BackOffice/Authorization/AuthorizationPolicies.cs b/src/Umbraco.Web.BackOffice/Authorization/AuthorizationPolicies.cs
index 7352a43b47..b6cad8dd06 100644
--- a/src/Umbraco.Web.BackOffice/Authorization/AuthorizationPolicies.cs
+++ b/src/Umbraco.Web.BackOffice/Authorization/AuthorizationPolicies.cs
@@ -2,31 +2,47 @@
{
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";
+ // TODO: Rethink these names! Describe what they are doing probably
+
+ public const string SectionAccessContentOrMedia = nameof(SectionAccessContentOrMedia);
+ public const string SectionAccessContent = nameof(SectionAccessContent);
+ public const string SectionAccessPackages = nameof(SectionAccessPackages);
+ public const string SectionAccessUsers = nameof(SectionAccessUsers);
+ public const string SectionAccessMedia = nameof(SectionAccessMedia);
+ public const string SectionAccessSettings = nameof(SectionAccessSettings);
+ public const string SectionAccessForTinyMce = nameof(SectionAccessForTinyMce);
+ public const string SectionAccessForMemberTree = nameof(SectionAccessForMemberTree);
+ public const string SectionAccessForMediaTree = nameof(SectionAccessForMediaTree);
+ public const string SectionAccessForContentTree = nameof(SectionAccessForContentTree);
+ public const string SectionAccessForDataTypeReading = nameof(SectionAccessForDataTypeReading);
+ public const string SectionAccessMembers = nameof(SectionAccessMembers);
+
+ public const string TreeAccessDocuments = nameof(TreeAccessDocuments);
+ public const string TreeAccessUsers = nameof(TreeAccessUsers);
+ public const string TreeAccessPartialViews = nameof(TreeAccessPartialViews);
+ public const string TreeAccessPartialViewMacros = nameof(TreeAccessPartialViewMacros);
+ public const string TreeAccessDataTypes = nameof(TreeAccessDataTypes);
+ public const string TreeAccessPackages = nameof(TreeAccessPackages);
+ public const string TreeAccessLogs = nameof(TreeAccessLogs);
+ public const string TreeAccessTemplates = nameof(TreeAccessTemplates);
+ public const string TreeAccessDictionary = nameof(TreeAccessDictionary);
+ public const string TreeAccessRelationTypes = nameof(TreeAccessRelationTypes);
+ public const string TreeAccessMediaTypes = nameof(TreeAccessMediaTypes);
+ public const string TreeAccessMacros = nameof(TreeAccessMacros);
+ public const string TreeAccessLanguages = nameof(TreeAccessLanguages);
+ public const string TreeAccessMemberGroups = nameof(TreeAccessMemberGroups);
+ public const string TreeAccessDocumentTypes = nameof(TreeAccessDocumentTypes);
+ public const string TreeAccessMemberTypes = nameof(TreeAccessMemberTypes);
+ public const string TreeAccessDocumentsOrDocumentTypes = nameof(TreeAccessDocumentsOrDocumentTypes);
+ public const string TreeAccessMediaOrMediaTypes = nameof(TreeAccessMediaOrMediaTypes);
+ public const string TreeAccessMembersOrMemberTypes = nameof(TreeAccessMembersOrMemberTypes);
+ public const string TreeAccessAnySchemaTypes = nameof(TreeAccessAnySchemaTypes);
+ public const string TreeAccessDictionaryOrTemplates = nameof(TreeAccessDictionaryOrTemplates);
///
/// 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)
///
- public const string TreeAccessAnyContentOrTypes = "TreeAccessAnyContentOrTypes";
+ public const string TreeAccessAnyContentOrTypes = nameof(TreeAccessAnyContentOrTypes);
}
}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/SectionAliasesRequirement.cs b/src/Umbraco.Web.BackOffice/Authorization/SectionAliasesRequirement.cs
new file mode 100644
index 0000000000..9789258bf8
--- /dev/null
+++ b/src/Umbraco.Web.BackOffice/Authorization/SectionAliasesRequirement.cs
@@ -0,0 +1,18 @@
+using Microsoft.AspNetCore.Authorization;
+using System.Collections.Generic;
+
+namespace Umbraco.Web.BackOffice.Authorization
+{
+ ///
+ /// Authorization requirements for
+ ///
+ public class SectionAliasesRequirement : IAuthorizationRequirement
+ {
+ ///
+ /// The aliases for sections that the user will need access to
+ ///
+ public IReadOnlyCollection SectionAliases { get; }
+
+ public SectionAliasesRequirement(params string[] aliases) => SectionAliases = aliases;
+ }
+}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/UmbracoSectionAuthorizeHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/UmbracoSectionAuthorizeHandler.cs
new file mode 100644
index 0000000000..b063c4c7d9
--- /dev/null
+++ b/src/Umbraco.Web.BackOffice/Authorization/UmbracoSectionAuthorizeHandler.cs
@@ -0,0 +1,47 @@
+using Microsoft.AspNetCore.Authorization;
+using System.Linq;
+using System.Threading.Tasks;
+using Umbraco.Core.Security;
+
+namespace Umbraco.Web.BackOffice.Authorization
+{
+
+ ///
+ /// Ensures that the current user has access to the section
+ ///
+ ///
+ /// The user only needs access to one of the sections specified, not all of the sections.
+ ///
+ public class UmbracoSectionAuthorizeHandler : AuthorizationHandler
+ {
+ private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
+
+ public UmbracoSectionAuthorizeHandler(IBackOfficeSecurityAccessor backofficeSecurityAccessor)
+ {
+ _backofficeSecurityAccessor = backofficeSecurityAccessor;
+ }
+
+ protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SectionAliasesRequirement requirement)
+ {
+ if (IsAuthorized(requirement))
+ {
+ context.Succeed(requirement);
+ }
+ else
+ {
+ context.Fail();
+ }
+
+ return Task.CompletedTask;
+ }
+
+ private bool IsAuthorized(SectionAliasesRequirement requirement)
+ {
+ var authorized = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser != null
+ && requirement.SectionAliases.Any(app => _backofficeSecurityAccessor.BackOfficeSecurity.UserHasSectionAccess(
+ app, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser));
+
+ return authorized;
+ }
+ }
+}
diff --git a/src/Umbraco.Web.BackOffice/Authorization/UmbracoTreeAuthorizeHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/UmbracoTreeAuthorizeHandler.cs
index b2cd18c1ce..9be8fd578f 100644
--- a/src/Umbraco.Web.BackOffice/Authorization/UmbracoTreeAuthorizeHandler.cs
+++ b/src/Umbraco.Web.BackOffice/Authorization/UmbracoTreeAuthorizeHandler.cs
@@ -8,8 +8,9 @@ using Umbraco.Web.Services;
namespace Umbraco.Web.BackOffice.Authorization
{
+
///
- /// Ensures that the current user has access to the application for which the specified tree(s) belongs
+ /// Ensures that the current user has access to the section for which the specified tree(s) belongs
///
///
/// This would allow a tree to be moved between sections.
@@ -17,10 +18,6 @@ namespace Umbraco.Web.BackOffice.Authorization
///
public class UmbracoTreeAuthorizeHandler : AuthorizationHandler
{
- ///
- /// Can be used by unit tests to enable/disable this filter
- ///
- internal static readonly bool Enable = true;
private readonly ITreeService _treeService;
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
@@ -40,25 +37,6 @@ namespace Umbraco.Web.BackOffice.Authorization
_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))
@@ -72,5 +50,19 @@ namespace Umbraco.Web.BackOffice.Authorization
return Task.CompletedTask;
}
+
+ private bool IsAuthorized(TreeAliasesRequirement requirement)
+ {
+ 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));
+ }
}
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs
index 4bf76a108a..118dbf0926 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Umbraco.Core;
@@ -14,6 +15,7 @@ using Umbraco.Core.Services;
using Umbraco.Core.Strings;
using Umbraco.Core.Strings.Css;
using Umbraco.Extensions;
+using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.ActionsResults;
using Umbraco.Web.Common.Attributes;
@@ -28,7 +30,7 @@ namespace Umbraco.Web.BackOffice.Controllers
// ref: https://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
//[PrefixlessBodyModelValidator]
- [UmbracoApplicationAuthorize(Constants.Applications.Settings)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)]
public class CodeFileController : BackOfficeNotificationsController
{
private readonly IIOHelper _ioHelper;
diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs
index ad51ea7dfd..f91bb9c124 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs
@@ -37,19 +37,16 @@ using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Common.Exceptions;
using Umbraco.Web.Common.Filters;
using Umbraco.Web.Models.Mapping;
-
+using Microsoft.AspNetCore.Authorization;
+using Umbraco.Web.BackOffice.Authorization;
namespace Umbraco.Web.BackOffice.Controllers
{
///
/// The API controller used for editing content
///
- ///
- /// This controller is decorated with the UmbracoApplicationAuthorizeAttribute which means that any user requesting
- /// access to ALL of the methods on this controller will need access to the content application.
- ///
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
- [UmbracoApplicationAuthorize(Constants.Applications.Content)]
+ [Authorize(Policy = AuthorizationPolicies.TreeAccessDocuments)]
public class ContentController : ContentControllerBase
{
private readonly PropertyEditorCollection _propertyEditors;
diff --git a/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs
index f8bc313bed..eb2108a5e2 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs
@@ -416,8 +416,7 @@ namespace Umbraco.Web.BackOffice.Controllers
///
/// Permission is granted to this method if the user has access to any of these sections: Content, media, settings, developer, members
///
- [UmbracoApplicationAuthorize(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
- Constants.Applications.Settings, Constants.Applications.Packages)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessForDataTypeReading)]
public IEnumerable GetAll()
{
return _dataTypeService
@@ -432,8 +431,7 @@ namespace Umbraco.Web.BackOffice.Controllers
///
/// Permission is granted to this method if the user has access to any of these sections: Content, media, settings, developer, members
///
- [UmbracoApplicationAuthorize(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
- Constants.Applications.Settings, Constants.Applications.Packages)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessForDataTypeReading)]
public IDictionary> GetGroupedDataTypes()
{
var dataTypes = _dataTypeService
@@ -464,8 +462,7 @@ namespace Umbraco.Web.BackOffice.Controllers
///
/// Permission is granted to this method if the user has access to any of these sections: Content, media, settings, developer, members
///
- [UmbracoApplicationAuthorize(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
- Constants.Applications.Settings, Constants.Applications.Packages)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessForDataTypeReading)]
public IDictionary> GetGroupedPropertyEditors()
{
var datatypes = new List();
@@ -496,8 +493,7 @@ namespace Umbraco.Web.BackOffice.Controllers
///
/// Permission is granted to this method if the user has access to any of these sections: Content, media, settings, developer, members
///
- [UmbracoApplicationAuthorize(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
- Constants.Applications.Settings, Constants.Applications.Packages)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessForDataTypeReading)]
public IEnumerable GetAllPropertyEditors()
{
return _propertyEditorCollection
diff --git a/src/Umbraco.Web.BackOffice/Controllers/LogController.cs b/src/Umbraco.Web.BackOffice/Controllers/LogController.cs
index 4d816624de..a8210eb20b 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/LogController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/LogController.cs
@@ -1,4 +1,5 @@
-using System;
+using Microsoft.AspNetCore.Authorization;
+using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core;
@@ -10,6 +11,7 @@ using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
+using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Models.ContentEditing;
@@ -52,7 +54,7 @@ namespace Umbraco.Web.BackOffice.Controllers
_sqlContext = sqlContext ?? throw new ArgumentNullException(nameof(sqlContext));
}
- [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Content, Constants.Applications.Media)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessContentOrMedia)]
public PagedResult GetPagedEntityLog(int id,
int pageNumber = 1,
int pageSize = 10,
diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs
index 0e97ed84af..5d8b9c0e97 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs
@@ -40,6 +40,8 @@ using Umbraco.Web.Common.Exceptions;
using Umbraco.Web.ContentApps;
using Umbraco.Web.Models.ContentEditing;
using Constants = Umbraco.Core.Constants;
+using Umbraco.Web.BackOffice.Authorization;
+using Microsoft.AspNetCore.Authorization;
namespace Umbraco.Web.BackOffice.Controllers
{
@@ -48,7 +50,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// access to ALL of the methods on this controller will need access to the media application.
///
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
- [UmbracoApplicationAuthorize(Constants.Applications.Media)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessMedia)]
public class MediaController : ContentControllerBase
{
private readonly IShortStringHelper _shortStringHelper;
diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
index 55042d458b..0b090a5e47 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
@@ -7,6 +7,7 @@ using System.Net.Http;
using System.Net.Mime;
using System.Text;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -24,6 +25,7 @@ using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
using Umbraco.Core.Strings;
using Umbraco.Extensions;
+using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.BackOffice.ModelBinders;
using Umbraco.Web.Common.Attributes;
@@ -41,7 +43,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// access to ALL of the methods on this controller will need access to the member application.
///
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
- [UmbracoApplicationAuthorize(Constants.Applications.Members)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessMembers)]
[OutgoingNoHyphenGuidFormat]
public class MemberController : ContentControllerBase
{
diff --git a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs
index 69d23d606b..de855ab421 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs
@@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Text;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using Semver;
@@ -12,6 +13,7 @@ using Umbraco.Core.Hosting;
using Umbraco.Core.Models.Packaging;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
+using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Common.Exceptions;
@@ -23,7 +25,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// A controller used for managing packages in the back office
///
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
- [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Packages)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessPackages)]
public class PackageController : UmbracoAuthorizedJsonController
{
private readonly IHostingEnvironment _hostingEnvironment;
diff --git a/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs b/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs
index 05631173c9..e08dfeff60 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs
@@ -22,6 +22,8 @@ using Umbraco.Web.Common.Exceptions;
using Umbraco.Web.Models;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Security;
+using Microsoft.AspNetCore.Authorization;
+using Umbraco.Web.BackOffice.Authorization;
namespace Umbraco.Web.BackOffice.Controllers
{
@@ -29,7 +31,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// A controller used for installing packages and managing all of the data in the packages section in the back office
///
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
- [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Packages)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessPackages)]
public class PackageInstallController : UmbracoAuthorizedJsonController
{
diff --git a/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs b/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs
index bf40e5722f..b8d5e1bc20 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs
@@ -3,11 +3,13 @@ using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
+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.ActionsResults;
using Umbraco.Web.Common.Attributes;
@@ -18,7 +20,7 @@ using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.BackOffice.Controllers
{
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
- [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Content)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessContent)]
public class RelationController : UmbracoAuthorizedJsonController
{
private readonly UmbracoMapper _umbracoMapper;
diff --git a/src/Umbraco.Web.BackOffice/Controllers/TinyMceController.cs b/src/Umbraco.Web.BackOffice/Controllers/TinyMceController.cs
index 4bae8970bc..8f7b5c5a0e 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/TinyMceController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/TinyMceController.cs
@@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
@@ -14,6 +15,7 @@ using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
using Umbraco.Core.Media;
using Umbraco.Core.Strings;
+using Umbraco.Web.BackOffice.Authorization;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.ActionsResults;
using Umbraco.Web.Common.Attributes;
@@ -23,10 +25,7 @@ using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.BackOffice.Controllers
{
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
- [UmbracoApplicationAuthorize(
- Constants.Applications.Content,
- Constants.Applications.Media,
- Constants.Applications.Members)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessForTinyMce)]
public class TinyMceController : UmbracoAuthorizedApiController
{
private readonly IHostingEnvironment _hostingEnvironment;
diff --git a/src/Umbraco.Web.BackOffice/Controllers/UserGroupsController.cs b/src/Umbraco.Web.BackOffice/Controllers/UserGroupsController.cs
index 4cba5064d1..940f2a76df 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/UserGroupsController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/UserGroupsController.cs
@@ -16,11 +16,13 @@ using Umbraco.Web.Common.Exceptions;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Security;
using Constants = Umbraco.Core.Constants;
+using Umbraco.Web.BackOffice.Authorization;
+using Microsoft.AspNetCore.Authorization;
namespace Umbraco.Web.BackOffice.Controllers
{
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
- [UmbracoApplicationAuthorize(Constants.Applications.Users)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessUsers)]
[PrefixlessBodyModelValidator]
public class UserGroupsController : UmbracoAuthorizedJsonController
{
diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs
index f187ec22ce..9da8e592a2 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs
@@ -42,11 +42,13 @@ using Task = System.Threading.Tasks.Task;
using Umbraco.Net;
using Umbraco.Web.Common.ActionsResults;
using Umbraco.Web.Common.Security;
+using Umbraco.Web.BackOffice.Authorization;
+using Microsoft.AspNetCore.Authorization;
namespace Umbraco.Web.BackOffice.Controllers
{
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
- [UmbracoApplicationAuthorize(Constants.Applications.Users)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessUsers)]
[PrefixlessBodyModelValidator]
[IsCurrentUserModelFilter]
public class UsersController : UmbracoAuthorizedJsonController
diff --git a/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs
index 7a77739342..00ab69f50e 100644
--- a/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs
+++ b/src/Umbraco.Web.BackOffice/Extensions/BackOfficeServiceCollectionExtensions.cs
@@ -109,10 +109,66 @@ namespace Umbraco.Extensions
return new BackOfficeIdentityBuilder(services);
}
+ ///
+ /// Add authorization handlers and policies
+ ///
+ ///
private static void AddBackOfficeAuthorizationPolicies(this IServiceCollection services)
{
+ services.AddSingleton();
+ services.AddSingleton();
+
services.AddAuthorization(options =>
{
+
+ options.AddPolicy(AuthorizationPolicies.SectionAccessContent, policy =>
+ policy.Requirements.Add(new SectionAliasesRequirement(Constants.Applications.Content)));
+
+ options.AddPolicy(AuthorizationPolicies.SectionAccessContentOrMedia, policy =>
+ policy.Requirements.Add(new SectionAliasesRequirement(Constants.Applications.Content, Constants.Applications.Media)));
+
+ options.AddPolicy(AuthorizationPolicies.SectionAccessUsers, policy =>
+ policy.Requirements.Add(new SectionAliasesRequirement(Constants.Applications.Users)));
+
+ options.AddPolicy(AuthorizationPolicies.SectionAccessForTinyMce, policy =>
+ policy.Requirements.Add(new SectionAliasesRequirement(
+ Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members)));
+
+ options.AddPolicy(AuthorizationPolicies.SectionAccessMedia, policy =>
+ policy.Requirements.Add(new SectionAliasesRequirement(Constants.Applications.Media)));
+
+ options.AddPolicy(AuthorizationPolicies.SectionAccessMembers, policy =>
+ policy.Requirements.Add(new SectionAliasesRequirement(Constants.Applications.Members)));
+
+ options.AddPolicy(AuthorizationPolicies.SectionAccessPackages, policy =>
+ policy.Requirements.Add(new SectionAliasesRequirement(Constants.Applications.Packages)));
+
+ options.AddPolicy(AuthorizationPolicies.SectionAccessSettings, policy =>
+ policy.Requirements.Add(new SectionAliasesRequirement(Constants.Applications.Settings)));
+
+ //We will not allow the tree to render unless the user has access to any of the sections that the tree gets rendered
+ // this is not ideal but until we change permissions to be tree based (not section) there's not much else we can do here.
+ options.AddPolicy(AuthorizationPolicies.SectionAccessForContentTree, policy =>
+ policy.Requirements.Add(new SectionAliasesRequirement(
+ Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Users,
+ Constants.Applications.Settings, Constants.Applications.Packages, Constants.Applications.Members)));
+ options.AddPolicy(AuthorizationPolicies.SectionAccessForMediaTree, policy =>
+ policy.Requirements.Add(new SectionAliasesRequirement(
+ Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Users,
+ Constants.Applications.Settings, Constants.Applications.Packages, Constants.Applications.Members)));
+ options.AddPolicy(AuthorizationPolicies.SectionAccessForMemberTree, policy =>
+ policy.Requirements.Add(new SectionAliasesRequirement(
+ Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members)));
+
+ // Permission is granted to this policy if the user has access to any of these sections: Content, media, settings, developer, members
+ options.AddPolicy(AuthorizationPolicies.SectionAccessForDataTypeReading, policy =>
+ policy.Requirements.Add(new SectionAliasesRequirement(
+ Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
+ Constants.Applications.Settings, Constants.Applications.Packages)));
+
+ options.AddPolicy(AuthorizationPolicies.TreeAccessDocuments, policy =>
+ policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Content)));
+
options.AddPolicy(AuthorizationPolicies.TreeAccessUsers, policy =>
policy.Requirements.Add(new TreeAliasesRequirement(Constants.Trees.Users)));
@@ -182,8 +238,6 @@ namespace Umbraco.Extensions
Constants.Trees.MediaTypes, Constants.Trees.Media,
Constants.Trees.MemberTypes, Constants.Trees.Members)));
});
-
- services.AddSingleton();
}
}
}
diff --git a/src/Umbraco.Web.BackOffice/Filters/UmbracoApplicationAuthorizeAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/UmbracoApplicationAuthorizeAttribute.cs
deleted file mode 100644
index cc1bdd4cd5..0000000000
--- a/src/Umbraco.Web.BackOffice/Filters/UmbracoApplicationAuthorizeAttribute.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System.Linq;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Filters;
-using Umbraco.Core.Security;
-using Umbraco.Web.Security;
-
-namespace Umbraco.Web.BackOffice.Filters
-{
- public class UmbracoApplicationAuthorizeAttribute : TypeFilterAttribute
- {
- public UmbracoApplicationAuthorizeAttribute(params string[] appName) : base(typeof(UmbracoApplicationAuthorizeFilter))
- {
- base.Arguments = new object[]
- {
- appName
- };
- }
-
- private class UmbracoApplicationAuthorizeFilter : IAuthorizationFilter
- {
- ///
- /// Can be used by unit tests to enable/disable this filter
- ///
- internal static bool Enable = true;
-
- private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
- private readonly string[] _appNames;
-
- ///
- /// Constructor to set any number of applications that the user needs access to be authorized
- ///
- ///
- ///
- /// If the user has access to any of the specified apps, they will be authorized.
- ///
- public UmbracoApplicationAuthorizeFilter(IBackOfficeSecurityAccessor backofficeSecurityAccessor, params string[] appName)
- {
- _backofficeSecurityAccessor = backofficeSecurityAccessor;
- _appNames = appName;
- }
-
-
- public void OnAuthorization(AuthorizationFilterContext context)
- {
- if (!IsAuthorized())
- {
- context.Result = new ForbidResult();
- }
- }
-
- private bool IsAuthorized()
- {
- if (Enable == false)
- {
- return true;
- }
-
- var authorized = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser != null
- && _appNames.Any(app => _backofficeSecurityAccessor.BackOfficeSecurity.UserHasSectionAccess(
- app, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser));
-
- return authorized;
- }
- }
- }
-
-}
diff --git a/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs b/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs
index 768ede1787..44d8161519 100644
--- a/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs
+++ b/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs
@@ -10,6 +10,8 @@ using Umbraco.Web.BackOffice.Controllers;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Microsoft.Extensions.Logging;
+using Umbraco.Web.BackOffice.Authorization;
+using Microsoft.AspNetCore.Authorization;
namespace Umbraco.Web.BackOffice.HealthCheck
{
@@ -17,7 +19,7 @@ namespace Umbraco.Web.BackOffice.HealthCheck
/// The API controller used to display the health check info and execute any actions
///
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
- [UmbracoApplicationAuthorize(Constants.Applications.Settings)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)]
public class HealthCheckController : UmbracoAuthorizedJsonController
{
private readonly HealthCheckCollection _checks;
diff --git a/src/Umbraco.Web.BackOffice/Profiling/WebProfilingController.cs b/src/Umbraco.Web.BackOffice/Profiling/WebProfilingController.cs
index 101e00d752..ae936984b0 100644
--- a/src/Umbraco.Web.BackOffice/Profiling/WebProfilingController.cs
+++ b/src/Umbraco.Web.BackOffice/Profiling/WebProfilingController.cs
@@ -4,6 +4,8 @@ using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.BackOffice.Controllers;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Common.Controllers;
+using Microsoft.AspNetCore.Authorization;
+using Umbraco.Web.BackOffice.Authorization;
namespace Umbraco.Web.BackOffice.Profiling
{
@@ -11,7 +13,7 @@ namespace Umbraco.Web.BackOffice.Profiling
/// The API controller used to display the state of the web profiler
///
[PluginController(Constants.Web.Mvc.BackOfficeApiArea)]
- [UmbracoApplicationAuthorize(Constants.Applications.Settings)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessSettings)]
public class WebProfilingController : UmbracoAuthorizedJsonController
{
private readonly IHostingEnvironment _hosting;
diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentBlueprintTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ContentBlueprintTreeController.cs
index 5477797ccc..eba16f288f 100644
--- a/src/Umbraco.Web.BackOffice/Trees/ContentBlueprintTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/ContentBlueprintTreeController.cs
@@ -1,11 +1,13 @@
using System;
using System.Linq;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
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;
@@ -21,7 +23,7 @@ namespace Umbraco.Web.BackOffice.Trees
///
/// This authorizes based on access to the content section even though it exists in the settings
///
- [UmbracoApplicationAuthorize(Constants.Applications.Content)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessContent)]
[Tree(Constants.Applications.Settings, Constants.Trees.ContentBlueprints, SortOrder = 12, TreeGroup = Constants.Trees.Groups.Settings)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]
diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs
index 755acdf0cb..6bc19e058c 100644
--- a/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs
@@ -12,29 +12,20 @@ using Umbraco.Web.Actions;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Search;
-using Umbraco.Core.Configuration;
using Umbraco.Core.Security;
using Constants = Umbraco.Core.Constants;
-using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Common.Exceptions;
-using Umbraco.Web.Security;
using Umbraco.Web.WebApi;
using Umbraco.Core.Configuration.Models;
using Microsoft.Extensions.Options;
using Umbraco.Web.Trees;
+using Microsoft.AspNetCore.Authorization;
+using Umbraco.Web.BackOffice.Authorization;
namespace Umbraco.Web.BackOffice.Trees
{
- //We will not allow the tree to render unless the user has access to any of the sections that the tree gets rendered
- // this is not ideal but until we change permissions to be tree based (not section) there's not much else we can do here.
- [UmbracoApplicationAuthorize(
- Constants.Applications.Content,
- Constants.Applications.Media,
- Constants.Applications.Users,
- Constants.Applications.Settings,
- Constants.Applications.Packages,
- Constants.Applications.Members)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessForContentTree)]
[Tree(Constants.Applications.Content, Constants.Trees.Content)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]
diff --git a/src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs
index ab332e3843..2ae60047a3 100644
--- a/src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs
@@ -20,17 +20,12 @@ using Umbraco.Web.Common.Exceptions;
using Umbraco.Web.Security;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
+using Microsoft.AspNetCore.Authorization;
+using Umbraco.Web.BackOffice.Authorization;
namespace Umbraco.Web.BackOffice.Trees
{
- //We will not allow the tree to render unless the user has access to any of the sections that the tree gets rendered
- // this is not ideal but until we change permissions to be tree based (not section) there's not much else we can do here.
- [UmbracoApplicationAuthorize(
- Constants.Applications.Content,
- Constants.Applications.Media,
- Constants.Applications.Settings,
- Constants.Applications.Packages,
- Constants.Applications.Members)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessForMediaTree)]
[Tree(Constants.Applications.Media, Constants.Trees.Media)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]
diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs
index 31ab66908c..e1b898c142 100644
--- a/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs
@@ -20,15 +20,12 @@ using Constants = Umbraco.Core.Constants;
using Umbraco.Web.Security;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
+using Umbraco.Web.BackOffice.Authorization;
+using Microsoft.AspNetCore.Authorization;
namespace Umbraco.Web.BackOffice.Trees
{
- //We will not allow the tree to render unless the user has access to any of the sections that the tree gets rendered
- // this is not ideal but until we change permissions to be tree based (not section) there's not much else we can do here.
- [UmbracoApplicationAuthorize(
- Constants.Applications.Content,
- Constants.Applications.Media,
- Constants.Applications.Members)]
+ [Authorize(Policy = AuthorizationPolicies.SectionAccessForMemberTree)]
[Tree(Constants.Applications.Members, Constants.Trees.Members, SortOrder = 0)]
[PluginController(Constants.Web.Mvc.BackOfficeTreeArea)]
[CoreTree]