globalSettings)
- {
- _globalSettings = globalSettings.Value;
- }
-
- public void OnAuthorization(AuthorizationFilterContext context)
- {
- var request = context.HttpContext.Request;
- if (_globalSettings.UseHttps && request.Scheme != Uri.UriSchemeHttps)
- {
- var uri = new UriBuilder()
- {
- Scheme = Uri.UriSchemeHttps,
- Host = request.Host.Value,
- Path = request.Path,
- Query = request.QueryString.ToUriComponent(),
- Port = 443
- };
- var body = string.Format("The resource can be found at {0}.
",
- uri.Uri.AbsoluteUri);
- if (request.Method.Equals(HttpMethod.Get.ToString()) || request.Method.Equals(HttpMethod.Head.ToString()))
- {
- context.HttpContext.Response.Headers.Add("Location", uri.Uri.ToString());
- context.Result = new ObjectResult(body)
- {
- StatusCode = (int)HttpStatusCode.Found,
- };
-
- }
- else
- {
- context.Result = new ObjectResult(body)
- {
- StatusCode = (int)HttpStatusCode.NotFound
- };
- }
-
-
- }
- }
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Filters/UserGroupAuthorizationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/UserGroupAuthorizationAttribute.cs
deleted file mode 100644
index 5fa9dd54be..0000000000
--- a/src/Umbraco.Web.BackOffice/Filters/UserGroupAuthorizationAttribute.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-using System.Linq;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Filters;
-using Umbraco.Core;
-using Umbraco.Core.Security;
-using Umbraco.Core.Services;
-using Umbraco.Web.BackOffice.Controllers;
-using Umbraco.Web.Security;
-
-namespace Umbraco.Web.BackOffice.Filters
-{
- ///
- /// Authorizes that the current user has access to the user group Id in the request
- ///
- ///
- /// This will authorize against one or multiple ids
- ///
- public sealed class UserGroupAuthorizationAttribute : TypeFilterAttribute
- {
-
- public UserGroupAuthorizationAttribute(string parameterName): base(typeof(UserGroupAuthorizationFilter))
- {
- Arguments = new object[] { parameterName };
- }
-
- public UserGroupAuthorizationAttribute() : this("id")
- {
- }
-
- private class UserGroupAuthorizationFilter : IAuthorizationFilter
- {
- private readonly string _parameterName;
- private readonly IRequestAccessor _requestAccessor;
- private readonly IUserService _userService;
- private readonly IContentService _contentService;
- private readonly IMediaService _mediaService;
- private readonly IEntityService _entityService;
- private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
-
- public UserGroupAuthorizationFilter(
- IRequestAccessor requestAccessor,
- IUserService userService,
- IContentService contentService,
- IMediaService mediaService,
- IEntityService entityService,
- IBackOfficeSecurityAccessor backofficeSecurityAccessor,
- string parameterName)
- {
- _requestAccessor = requestAccessor;
- _userService = userService;
- _contentService = contentService;
- _mediaService = mediaService;
- _entityService = entityService;
- _backofficeSecurityAccessor = backofficeSecurityAccessor;
- _parameterName = parameterName;
- }
-
- public void OnAuthorization(AuthorizationFilterContext context)
- {
- if (!IsAuthorized(context))
- {
- context.Result = new ForbidResult();
- }
- }
-
- private bool IsAuthorized(AuthorizationFilterContext actionContext)
- {
- var currentUser = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser;
-
- var queryString = actionContext.HttpContext.Request.Query;
-
- var ids = queryString.Where(x => x.Key == _parameterName).ToArray();
- if (ids.Length == 0)
- return true;
-
- var intIds = ids.Select(x => x.Value.TryConvertTo()).Where(x => x.Success).Select(x => x.Result).ToArray();
- var authHelper = new UserGroupEditorAuthorizationHelper(
- _userService,
- _contentService,
- _mediaService,
- _entityService);
- return authHelper.AuthorizeGroupAccess(currentUser, intIds);
-
- }
- }
- }
-}
diff --git a/src/Umbraco.Web.BackOffice/Filters/ValidateAngularAntiForgeryTokenAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/ValidateAngularAntiForgeryTokenAttribute.cs
index 556a84e0dc..f7fd174e03 100644
--- a/src/Umbraco.Web.BackOffice/Filters/ValidateAngularAntiForgeryTokenAttribute.cs
+++ b/src/Umbraco.Web.BackOffice/Filters/ValidateAngularAntiForgeryTokenAttribute.cs
@@ -53,13 +53,13 @@ namespace Umbraco.Web.BackOffice.Filters
var cookieToken = _cookieManager.GetCookieValue(Constants.Web.CsrfValidationCookieName);
var httpContext = context.HttpContext;
- var validateResult = await ValidateHeaders(httpContext, cookieToken);
- if (validateResult.Item1 == false)
- {
- httpContext.SetReasonPhrase(validateResult.Item2);
- context.Result = new StatusCodeResult((int)HttpStatusCode.ExpectationFailed);
- return;
- }
+ var validateResult = await ValidateHeaders(httpContext, cookieToken);
+ if (validateResult.Item1 == false)
+ {
+ httpContext.SetReasonPhrase(validateResult.Item2);
+ context.Result = new StatusCodeResult((int)HttpStatusCode.ExpectationFailed);
+ return;
+ }
await next();
}
diff --git a/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs b/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs
index 768ede1787..002cfe4d7b 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 Microsoft.AspNetCore.Authorization;
+using Umbraco.Web.Common.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..0908522d9e 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.Common.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..e232bf03b9 100644
--- a/src/Umbraco.Web.BackOffice/Trees/ContentBlueprintTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/ContentBlueprintTreeController.cs
@@ -1,5 +1,6 @@
using System;
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.Services;
using Umbraco.Web.Actions;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
+using Umbraco.Web.Common.Authorization;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
@@ -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..16dd446d49 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.Common.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/ContentTreeControllerBase.cs b/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs
index 36b9f7f5de..53a6f02a79 100644
--- a/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs
@@ -123,7 +123,7 @@ namespace Umbraco.Web.BackOffice.Trees
internal TreeNode GetSingleTreeNodeWithAccessCheck(IEntitySlim e, string parentId, FormCollection queryStrings,
int[] startNodeIds, string[] startNodePaths)
{
- var entityIsAncestorOfStartNodes = ContentPermissionsHelper.IsInBranchOfStartNode(e.Path, startNodeIds, startNodePaths, out var hasPathAccess);
+ var entityIsAncestorOfStartNodes = ContentPermissions.IsInBranchOfStartNode(e.Path, startNodeIds, startNodePaths, out var hasPathAccess);
var ignoreUserStartNodes = IgnoreUserStartNodes(queryStrings);
if (ignoreUserStartNodes == false && entityIsAncestorOfStartNodes == false)
return null;
diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs
index 5c8312c058..8b5286bdd2 100644
--- a/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
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.Services;
using Umbraco.Web.Actions;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
+using Umbraco.Web.Common.Authorization;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Search;
@@ -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]
diff --git a/src/Umbraco.Web.BackOffice/Trees/DataTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/DataTypeTreeController.cs
index 3502d7a506..ab2bfdb8d4 100644
--- a/src/Umbraco.Web.BackOffice/Trees/DataTypeTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/DataTypeTreeController.cs
@@ -14,10 +14,12 @@ using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
+using Microsoft.AspNetCore.Authorization;
+using Umbraco.Web.Common.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]
diff --git a/src/Umbraco.Web.BackOffice/Trees/DictionaryTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/DictionaryTreeController.cs
index a9878a3dbc..ffa9e00b0e 100644
--- a/src/Umbraco.Web.BackOffice/Trees/DictionaryTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/DictionaryTreeController.cs
@@ -1,5 +1,6 @@
using System;
using System.Linq;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Umbraco.Core;
using Umbraco.Core.Models;
@@ -7,6 +8,7 @@ using Umbraco.Core.Services;
using Umbraco.Web.Actions;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
+using Umbraco.Web.Common.Authorization;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
@@ -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
- )]
+ // 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)]
diff --git a/src/Umbraco.Web.BackOffice/Trees/LanguageTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/LanguageTreeController.cs
index d4a2c91fad..ecd1c954ac 100644
--- a/src/Umbraco.Web.BackOffice/Trees/LanguageTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/LanguageTreeController.cs
@@ -1,7 +1,9 @@
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
+using Umbraco.Web.Common.Authorization;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
@@ -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]
diff --git a/src/Umbraco.Web.BackOffice/Trees/LogViewerTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/LogViewerTreeController.cs
index 9ffe6e8b68..b03b2d9926 100644
--- a/src/Umbraco.Web.BackOffice/Trees/LogViewerTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/LogViewerTreeController.cs
@@ -1,7 +1,9 @@
-using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
+using Umbraco.Web.Common.Authorization;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
@@ -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]
diff --git a/src/Umbraco.Web.BackOffice/Trees/MacrosTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MacrosTreeController.cs
index 567f0a3646..518c1b5495 100644
--- a/src/Umbraco.Web.BackOffice/Trees/MacrosTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/MacrosTreeController.cs
@@ -8,10 +8,12 @@ using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
using Constants = Umbraco.Core.Constants;
+using Microsoft.AspNetCore.Authorization;
+using Umbraco.Web.Common.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]
diff --git a/src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs
index ab332e3843..ece4013d0b 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.Common.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/MediaTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MediaTypeTreeController.cs
index 4df81b7023..cd64e23067 100644
--- a/src/Umbraco.Web.BackOffice/Trees/MediaTypeTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/MediaTypeTreeController.cs
@@ -13,10 +13,12 @@ using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
+using Microsoft.AspNetCore.Authorization;
+using Umbraco.Web.Common.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]
diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs
index 17de4ca37d..817b32f301 100644
--- a/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs
@@ -1,17 +1,19 @@
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.Filters;
using Umbraco.Web.Common.Attributes;
+using Umbraco.Web.Common.Authorization;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Trees;
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]
diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs
index 31ab66908c..4ebd8f7cc5 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 Microsoft.AspNetCore.Authorization;
+using Umbraco.Web.Common.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]
diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MemberTypeTreeController.cs
index c9e340617e..be400bef39 100644
--- a/src/Umbraco.Web.BackOffice/Trees/MemberTypeTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/MemberTypeTreeController.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Umbraco.Core;
using Umbraco.Core.Models;
@@ -7,6 +8,7 @@ using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.BackOffice.Trees;
using Umbraco.Web.Common.Attributes;
+using Umbraco.Web.Common.Authorization;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Search;
@@ -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
diff --git a/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs
index fc1f3d876f..5c96bb4d64 100644
--- a/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs
@@ -1,7 +1,9 @@
-using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
+using Umbraco.Web.Common.Authorization;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
@@ -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]
diff --git a/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs
index 5baeac7d17..484ea21b2f 100644
--- a/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs
@@ -1,7 +1,9 @@
-using Umbraco.Core.IO;
+using Microsoft.AspNetCore.Authorization;
+using Umbraco.Core.IO;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
+using Umbraco.Web.Common.Authorization;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
using Constants = Umbraco.Core.Constants;
@@ -12,7 +14,7 @@ namespace Umbraco.Web.BackOffice.Trees
/// Tree for displaying partial view macros in the developer app
///
[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
diff --git a/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs
index dcb98ac5b4..b648bd797f 100644
--- a/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs
@@ -1,7 +1,9 @@
-using Umbraco.Core.IO;
+using Microsoft.AspNetCore.Authorization;
+using Umbraco.Core.IO;
using Umbraco.Core.Services;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
+using Umbraco.Web.Common.Authorization;
using Umbraco.Web.Composing;
using Umbraco.Web.Mvc;
using Umbraco.Web.Trees;
@@ -14,7 +16,7 @@ namespace Umbraco.Web.BackOffice.Trees
/// Tree for displaying partial views in the settings app
///
[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
diff --git a/src/Umbraco.Web.BackOffice/Trees/RelationTypeTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/RelationTypeTreeController.cs
index 965e957f02..a36c2f36a9 100644
--- a/src/Umbraco.Web.BackOffice/Trees/RelationTypeTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/RelationTypeTreeController.cs
@@ -9,10 +9,12 @@ using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
+using Microsoft.AspNetCore.Authorization;
+using Umbraco.Web.Common.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]
diff --git a/src/Umbraco.Web.BackOffice/Trees/TemplatesTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/TemplatesTreeController.cs
index cfe2d57ce5..361875a41b 100644
--- a/src/Umbraco.Web.BackOffice/Trees/TemplatesTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/TemplatesTreeController.cs
@@ -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;
@@ -10,6 +11,7 @@ using Umbraco.Extensions;
using Umbraco.Web.Actions;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.Attributes;
+using Umbraco.Web.Common.Authorization;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Search;
@@ -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]
diff --git a/src/Umbraco.Web.BackOffice/Trees/UserTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/UserTreeController.cs
index feb8d2a9ec..960ed76ac5 100644
--- a/src/Umbraco.Web.BackOffice/Trees/UserTreeController.cs
+++ b/src/Umbraco.Web.BackOffice/Trees/UserTreeController.cs
@@ -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.Common.Attributes;
+using Umbraco.Web.Common.Authorization;
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]
diff --git a/src/Umbraco.Web.Common/Authorization/AuthorizationPolicies.cs b/src/Umbraco.Web.Common/Authorization/AuthorizationPolicies.cs
new file mode 100644
index 0000000000..56070f5033
--- /dev/null
+++ b/src/Umbraco.Web.Common/Authorization/AuthorizationPolicies.cs
@@ -0,0 +1,82 @@
+namespace Umbraco.Web.Common.Authorization
+{
+ ///
+ /// A list of authorization policy names for use in the back office
+ ///
+ public static class AuthorizationPolicies
+ {
+ public const string UmbracoFeatureEnabled = nameof(UmbracoFeatureEnabled);
+
+ public const string BackOfficeAccess = nameof(BackOfficeAccess);
+ public const string BackOfficeAccessWithoutApproval = nameof(BackOfficeAccessWithoutApproval);
+ public const string UserBelongsToUserGroupInRequest = nameof(UserBelongsToUserGroupInRequest);
+ public const string AdminUserEditsRequireAdmin = nameof(AdminUserEditsRequireAdmin);
+ public const string DenyLocalLoginIfConfigured = nameof(DenyLocalLoginIfConfigured);
+
+ // Content permission access
+
+ public const string ContentPermissionByResource = nameof(ContentPermissionByResource);
+ public const string ContentPermissionEmptyRecycleBin = nameof(ContentPermissionEmptyRecycleBin);
+ public const string ContentPermissionAdministrationById = nameof(ContentPermissionAdministrationById);
+ public const string ContentPermissionPublishById = nameof(ContentPermissionPublishById);
+ public const string ContentPermissionRollbackById = nameof(ContentPermissionRollbackById);
+ public const string ContentPermissionProtectById = nameof(ContentPermissionProtectById);
+ public const string ContentPermissionBrowseById = nameof(ContentPermissionBrowseById);
+ public const string ContentPermissionDeleteById = nameof(ContentPermissionDeleteById);
+
+ public const string MediaPermissionByResource = nameof(MediaPermissionByResource);
+ public const string MediaPermissionPathById = nameof(MediaPermissionPathById);
+
+
+ // Single section access
+
+ 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 SectionAccessMembers = nameof(SectionAccessMembers);
+
+ // Custom access based on multiple sections
+
+ public const string SectionAccessContentOrMedia = nameof(SectionAccessContentOrMedia);
+ 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);
+
+ // Single tree access
+
+ 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);
+
+ // Custom access based on multiple trees
+
+ 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 = nameof(TreeAccessAnyContentOrTypes);
+ }
+}
diff --git a/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeHandler.cs b/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeHandler.cs
new file mode 100644
index 0000000000..3f2fc0b6bb
--- /dev/null
+++ b/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeHandler.cs
@@ -0,0 +1,48 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using System;
+using System.Threading.Tasks;
+using Umbraco.Web.Features;
+
+namespace Umbraco.Web.BackOffice.Authorization
+{
+ ///
+ /// Ensures that the controller is an authorized feature.
+ ///
+ public class FeatureAuthorizeHandler : AuthorizationHandler
+ {
+ private readonly UmbracoFeatures _umbracoFeatures;
+
+ public FeatureAuthorizeHandler(UmbracoFeatures umbracoFeatures)
+ {
+ _umbracoFeatures = umbracoFeatures;
+ }
+
+ protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FeatureAuthorizeRequirement requirement)
+ {
+ var allowed = IsAllowed(context);
+ if (!allowed.HasValue || allowed.Value)
+ {
+ context.Succeed(requirement);
+ }
+ else
+ {
+ context.Fail();
+ }
+ return Task.CompletedTask;
+ }
+
+ private bool? IsAllowed(AuthorizationHandlerContext context)
+ {
+ if (!(context.Resource is Endpoint endpoint))
+ {
+ throw new InvalidOperationException("This authorization handler can only be applied to controllers routed with endpoint routing");
+ }
+
+ var actionDescriptor = endpoint.Metadata.GetMetadata();
+ var controllerType = actionDescriptor.ControllerTypeInfo.AsType();
+ return _umbracoFeatures.IsControllerEnabled(controllerType);
+ }
+ }
+}
diff --git a/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeRequirement.cs b/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeRequirement.cs
new file mode 100644
index 0000000000..87614d7f19
--- /dev/null
+++ b/src/Umbraco.Web.Common/Authorization/FeatureAuthorizeRequirement.cs
@@ -0,0 +1,12 @@
+using Microsoft.AspNetCore.Authorization;
+
+namespace Umbraco.Web.BackOffice.Authorization
+{
+
+ ///
+ /// Authorization requirement for the
+ ///
+ public class FeatureAuthorizeRequirement : IAuthorizationRequirement
+ {
+ }
+}
diff --git a/src/Umbraco.Web.Common/Controllers/UmbracoApiController.cs b/src/Umbraco.Web.Common/Controllers/UmbracoApiController.cs
index c53b08d3df..e3250c2983 100644
--- a/src/Umbraco.Web.Common/Controllers/UmbracoApiController.cs
+++ b/src/Umbraco.Web.Common/Controllers/UmbracoApiController.cs
@@ -7,6 +7,8 @@ namespace Umbraco.Web.Common.Controllers
///
public abstract class UmbracoApiController : UmbracoApiControllerBase, IDiscoverable
{
+ // TODO: Should this only exist in the back office project? These really are only ever used for the back office AFAIK
+
protected UmbracoApiController()
{
}
diff --git a/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerBase.cs b/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerBase.cs
index 787da05ca4..9f9e2b19be 100644
--- a/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerBase.cs
+++ b/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerBase.cs
@@ -1,8 +1,9 @@
-using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
using Umbraco.Web.Common.Attributes;
+using Umbraco.Web.Common.Authorization;
using Umbraco.Web.Common.Filters;
using Umbraco.Web.Features;
-using Umbraco.Web.WebApi.Filters;
namespace Umbraco.Web.Common.Controllers
{
@@ -13,11 +14,13 @@ namespace Umbraco.Web.Common.Controllers
/// These controllers are NOT auto-routed.
/// The base class is which are netcore API controllers without any view support
///
- [FeatureAuthorize] // TODO: This could be part of our conventions
+ [Authorize(Policy = AuthorizationPolicies.UmbracoFeatureEnabled)] // TODO: This could be part of our conventions
[TypeFilter(typeof(HttpResponseExceptionFilter))] // TODO: This could be part of our conventions
[UmbracoApiController]
public abstract class UmbracoApiControllerBase : ControllerBase, IUmbracoFeature
{
+ // TODO: Should this only exist in the back office project? These really are only ever used for the back office AFAIK
+
public UmbracoApiControllerBase()
{
}
diff --git a/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs b/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs
index 4bffb0d5ba..8d68e95dd8 100644
--- a/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs
+++ b/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs
@@ -5,6 +5,8 @@ namespace Umbraco.Web.Common.Controllers
{
public class UmbracoApiControllerTypeCollectionBuilder : TypeCollectionBuilderBase
{
+ // TODO: Should this only exist in the back office project? These really are only ever used for the back office AFAIK
+
protected override UmbracoApiControllerTypeCollectionBuilder This => this;
}
}
diff --git a/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs b/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs
index 596c42e3b3..caf4132664 100644
--- a/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs
+++ b/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs
@@ -1,5 +1,4 @@
using System;
-using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -8,9 +7,7 @@ using Smidge;
using Smidge.Nuglify;
using StackExchange.Profiling;
using Umbraco.Core;
-using Umbraco.Core.Composing;
using Umbraco.Core.Hosting;
-using Umbraco.Core.Runtime;
using Umbraco.Infrastructure.Logging.Serilog.Enrichers;
using Umbraco.Web.Common.Middleware;
diff --git a/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000000..e8e3e2c329
--- /dev/null
+++ b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs
@@ -0,0 +1,25 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.Extensions.DependencyInjection;
+using Umbraco.Web.BackOffice.Authorization;
+using Umbraco.Web.Common.Authorization;
+
+namespace Umbraco.Extensions
+{
+ public static class ServiceCollectionExtensions
+ {
+ public static void AddUmbracoCommonAuthorizationPolicies(this IServiceCollection services)
+ {
+ // TODO: Should this only exist in the back office project? These really are only ever used for the back office AFAIK
+ // If it is moved it should only target the back office scheme
+
+ services.AddSingleton();
+
+ services.AddAuthorization(options =>
+ {
+ options.AddPolicy(AuthorizationPolicies.UmbracoFeatureEnabled, policy =>
+ policy.Requirements.Add(new FeatureAuthorizeRequirement()));
+ });
+ }
+ }
+
+}
diff --git a/src/Umbraco.Web.Common/Filters/FeatureAuthorizeAttribute.cs b/src/Umbraco.Web.Common/Filters/FeatureAuthorizeAttribute.cs
deleted file mode 100644
index 061225334a..0000000000
--- a/src/Umbraco.Web.Common/Filters/FeatureAuthorizeAttribute.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-
-using System;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Controllers;
-using Microsoft.AspNetCore.Mvc.Filters;
-using Microsoft.AspNetCore.Mvc.Rendering;
-using Microsoft.Extensions.DependencyInjection;
-using Umbraco.Web.Features;
-using Umbraco.Core;
-using Umbraco.Web.Install;
-
-namespace Umbraco.Web.WebApi.Filters
-{
- ///
- /// Ensures that the controller is an authorized feature.
- ///
- /// Else returns unauthorized.
- public class FeatureAuthorizeAttribute : TypeFilterAttribute
- {
- public FeatureAuthorizeAttribute() : base(typeof(FeatureAuthorizeFilter))
- {
- }
-
- private class FeatureAuthorizeFilter : IAuthorizationFilter
- {
- public void OnAuthorization(AuthorizationFilterContext context)
- {
- var serviceProvider = context.HttpContext.RequestServices;
- var umbracoFeatures = serviceProvider.GetService();
-
- if (!IsAllowed(context, umbracoFeatures))
- {
- context.Result = new ForbidResult();
- }
- }
-
- private static bool IsAllowed(AuthorizationFilterContext context, UmbracoFeatures umbracoFeatures)
- {
- // if no features resolver has been set then return true, this will occur in unit
- // tests and we don't want users to have to set a resolver
- //just so their unit tests work.
-
- if (umbracoFeatures == null) return true;
- if (!(context.ActionDescriptor is ControllerActionDescriptor contextActionDescriptor)) return true;
-
- var controllerType = contextActionDescriptor.ControllerTypeInfo.AsType();
- return umbracoFeatures.IsControllerEnabled(controllerType);
- }
- }
- }
-}
diff --git a/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeAttribute.cs b/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeAttribute.cs
deleted file mode 100644
index 1f4abbaa25..0000000000
--- a/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeAttribute.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-
-namespace Umbraco.Web.Common.Filters
-{
- ///
- /// Ensures authorization is successful for a back office user.
- ///
- public class UmbracoBackOfficeAuthorizeAttribute : TypeFilterAttribute
- {
- ///
- /// Default constructor
- ///
- public UmbracoBackOfficeAuthorizeAttribute() : this(false, false)
- {
- }
-
- ///
- /// Constructor with redirect umbraco login behavior
- ///
- ///
- ///
-
- public UmbracoBackOfficeAuthorizeAttribute(bool redirectToUmbracoLogin, bool requireApproval) : base(typeof(UmbracoBackOfficeAuthorizeFilter))
- {
- Arguments = new object[] { redirectToUmbracoLogin, requireApproval };
- }
-
- ///
- /// Constructor with redirect url behavior
- ///
- ///
- public UmbracoBackOfficeAuthorizeAttribute(string redirectUrl) : base(typeof(UmbracoBackOfficeAuthorizeFilter))
- {
- Arguments = new object[] { redirectUrl };
- }
- }
-}
diff --git a/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeFilter.cs b/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeFilter.cs
deleted file mode 100644
index 0c30b25ced..0000000000
--- a/src/Umbraco.Web.Common/Filters/UmbracoBackOfficeAuthorizeFilter.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Filters;
-using Microsoft.AspNetCore.Routing;
-using System;
-using Umbraco.Core;
-using Umbraco.Core.Security;
-using Umbraco.Extensions;
-using Umbraco.Web.Security;
-using IHostingEnvironment = Umbraco.Core.Hosting.IHostingEnvironment;
-
-namespace Umbraco.Web.Common.Filters
-{
-
- ///
- /// Ensures authorization is successful for a back office user.
- ///
- public class UmbracoBackOfficeAuthorizeFilter : IAuthorizationFilter
- {
- private readonly bool _requireApproval;
-
- ///
- /// Can be used by unit tests to enable/disable this filter
- ///
- internal static bool Enable = true;
- private readonly IHostingEnvironment _hostingEnvironment;
- private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
- private readonly IRuntimeState _runtimeState;
- private readonly LinkGenerator _linkGenerator;
- private readonly bool _redirectToUmbracoLogin;
- private string _redirectUrl;
-
- private UmbracoBackOfficeAuthorizeFilter(
- IHostingEnvironment hostingEnvironment,
- IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
- IRuntimeState runtimeState,
- LinkGenerator linkGenerator,
- bool redirectToUmbracoLogin, bool requireApproval, string redirectUrl)
- {
- _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
- _backOfficeSecurityAccessor = backOfficeSecurityAccessor;
- _runtimeState = runtimeState ?? throw new ArgumentNullException(nameof(runtimeState));
- _linkGenerator = linkGenerator ?? throw new ArgumentNullException(nameof(linkGenerator));
- _redirectToUmbracoLogin = redirectToUmbracoLogin;
- _redirectUrl = redirectUrl;
- _requireApproval = requireApproval;
- }
-
- ///
- /// Default constructor
- ///
- ///
- ///
- ///
- ///
- ///
- public UmbracoBackOfficeAuthorizeFilter(
- IHostingEnvironment hostingEnvironment,
- IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
- IRuntimeState runtimeState, LinkGenerator linkGenerator,
- string redirectUrl) : this(hostingEnvironment, backOfficeSecurityAccessor, runtimeState, linkGenerator, false, false, redirectUrl)
- {
- }
-
- public UmbracoBackOfficeAuthorizeFilter(
- IHostingEnvironment hostingEnvironment,
- IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
- IRuntimeState runtimeState, LinkGenerator linkGenerator,
- bool redirectToUmbracoLogin, bool requireApproval) : this(hostingEnvironment, backOfficeSecurityAccessor, runtimeState, linkGenerator, redirectToUmbracoLogin, requireApproval, null)
- {
- }
-
- public void OnAuthorization(AuthorizationFilterContext context)
- {
- if (!IsAuthorized())
- {
- if (_redirectToUmbracoLogin)
- {
- _redirectUrl = _linkGenerator.GetBackOfficeUrl(_hostingEnvironment);
- }
-
- if (!_redirectUrl.IsNullOrWhiteSpace())
- {
- context.Result = new RedirectResult(_redirectUrl);
- }
- else
- {
- context.Result = new ForbidResult();
- }
- }
- }
-
- private bool IsAuthorized()
- {
- if (Enable == false)
- return true;
-
- try
- {
- // if not configured (install or upgrade) then we can continue
- // otherwise we need to ensure that a user is logged in
- return _runtimeState.Level == RuntimeLevel.Install
- || _runtimeState.Level == RuntimeLevel.Upgrade
- || _backOfficeSecurityAccessor?.BackOfficeSecurity.ValidateCurrentUser(false, _requireApproval) == ValidateRequestAttempt.Success;
- }
- catch (Exception)
- {
- return false;
- }
- }
- }
-}
diff --git a/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs b/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs
index 4019f462eb..fc247af55c 100644
--- a/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs
+++ b/src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs
@@ -9,10 +9,11 @@ namespace Umbraco.Web.Common.Filters
{
///
- /// Ensures authorization is successful for a back office user.
+ /// Ensures authorization is successful for a front-end member
///
public class UmbracoMemberAuthorizeFilter : IAuthorizationFilter
{
+ // TODO: Lets revisit this when we get members done and the front-end working and whether it can be replaced or moved to an authz policy
private readonly IUmbracoWebsiteSecurity _websiteSecurity;
public UmbracoMemberAuthorizeFilter(IUmbracoWebsiteSecurity websiteSecurity)
diff --git a/src/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringAttribute.cs b/src/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringAttribute.cs
index 45806b9d18..bbd3aa981e 100644
--- a/src/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringAttribute.cs
+++ b/src/Umbraco.Web.Common/Filters/ValidateUmbracoFormRouteStringAttribute.cs
@@ -22,6 +22,8 @@ namespace Umbraco.Web.Common.Filters
public class ValidateUmbracoFormRouteStringAttribute : TypeFilterAttribute
{
+ // TODO: Lets revisit this when we get members done and the front-end working and whether it can moved to an authz policy
+
public ValidateUmbracoFormRouteStringAttribute() : base(typeof(ValidateUmbracoFormRouteStringFilter))
{
Arguments = new object[] { };
diff --git a/src/Umbraco.Web.Common/Install/InstallAuthorizeAttribute.cs b/src/Umbraco.Web.Common/Install/InstallAuthorizeAttribute.cs
index bddb4fd015..04f743144e 100644
--- a/src/Umbraco.Web.Common/Install/InstallAuthorizeAttribute.cs
+++ b/src/Umbraco.Web.Common/Install/InstallAuthorizeAttribute.cs
@@ -13,6 +13,8 @@ namespace Umbraco.Web.Common.Install
///
public class InstallAuthorizeAttribute : TypeFilterAttribute
{
+ // NOTE: This doesn't need to be an authz policy, it's only used for the installer
+
public InstallAuthorizeAttribute() : base(typeof(InstallAuthorizeFilter))
{
}
diff --git a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs
index d5980c10b5..f20ab5ab75 100644
--- a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs
+++ b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs
@@ -97,8 +97,7 @@ namespace Umbraco.Web.Common.Runtime
builder.Services.AddUnique();
builder.Services.AddUnique();
- builder.Services.AddUnique();
- builder.Services.AddUnique(factory => new LegacyPasswordSecurity());
+ builder.Services.AddUnique();
}
}
}
diff --git a/src/Umbraco.Web.Website/Controllers/UmbracoAuthorizedController.cs b/src/Umbraco.Web.Website/Controllers/UmbracoAuthorizedController.cs
deleted file mode 100644
index b2611848df..0000000000
--- a/src/Umbraco.Web.Website/Controllers/UmbracoAuthorizedController.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using Umbraco.Web.Common.Filters;
-
-namespace Umbraco.Web.Mvc
-{
- ///
- /// Provides a base class for authorized Umbraco controllers.
- ///
- ///
- /// This controller essentially just uses a global UmbracoAuthorizeAttribute, inheritors that require more granular control over the
- /// authorization of each method can use this attribute instead of inheriting from this controller.
- ///
- [UmbracoBackOfficeAuthorize]
- [DisableBrowserCache]
- public abstract class UmbracoAuthorizedController : UmbracoController
- {
-
- }
-}