diff --git a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs
index 05c0513bed..2d0ffc5d33 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs
@@ -30,7 +30,7 @@ using Umbraco.Web.Editors;
// ref: https://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/
[PluginController("UmbracoApi")]
//[PrefixlessBodyModelValidator]
- [TypeFilter(typeof(UmbracoApplicationAuthorizeAttribute), Arguments = new object[]{new string []{Constants.Applications.Settings}})]
+ [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Settings)]
public class CodeFileController : BackOfficeNotificationsController
{
private readonly IIOHelper _ioHelper;
diff --git a/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs
index bdd1435b34..0d66a5e329 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs
@@ -28,7 +28,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// Content Types, Member Types or Media Types ... and of course to Data Types
///
[PluginController("UmbracoApi")]
- [TypeFilter(typeof(UmbracoTreeAuthorizeAttribute), Arguments = new object[]{new string[]{Constants.Trees.DataTypes, Constants.Trees.DocumentTypes, Constants.Trees.MediaTypes, Constants.Trees.MemberTypes}})]
+ [UmbracoTreeAuthorizeAttribute(Constants.Trees.DataTypes, Constants.Trees.DocumentTypes, Constants.Trees.MediaTypes, Constants.Trees.MemberTypes)]
public class DataTypeController : BackOfficeNotificationsController
{
private readonly PropertyEditorCollection _propertyEditors;
@@ -408,8 +408,8 @@ 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
///
- [TypeFilter(typeof(UmbracoApplicationAuthorizeAttribute), Arguments = new object[]{new string []{Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
- Constants.Applications.Settings, Constants.Applications.Packages}})]
+ [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
+ Constants.Applications.Settings, Constants.Applications.Packages)]
public IEnumerable GetAll()
{
return _dataTypeService
@@ -424,8 +424,8 @@ 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
///
- [TypeFilter(typeof(UmbracoTreeAuthorizeAttribute), Arguments = new object[]{new string[]{Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
- Constants.Applications.Settings, Constants.Applications.Packages}})]
+ [UmbracoTreeAuthorizeAttribute(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
+ Constants.Applications.Settings, Constants.Applications.Packages)]
public IDictionary> GetGroupedDataTypes()
{
var dataTypes = _dataTypeService
@@ -456,8 +456,8 @@ 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
///
- [TypeFilter(typeof(UmbracoTreeAuthorizeAttribute), Arguments = new object[]{new string[]{Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
- Constants.Applications.Settings, Constants.Applications.Packages}})]
+ [UmbracoTreeAuthorizeAttribute(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
+ Constants.Applications.Settings, Constants.Applications.Packages)]
public IDictionary> GetGroupedPropertyEditors()
{
@@ -489,8 +489,8 @@ 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
///
- [TypeFilter(typeof(UmbracoTreeAuthorizeAttribute), Arguments = new object[]{new string[]{Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
- Constants.Applications.Settings, Constants.Applications.Packages}})]
+ [UmbracoTreeAuthorizeAttribute(Constants.Applications.Content, Constants.Applications.Media, Constants.Applications.Members,
+ Constants.Applications.Settings, Constants.Applications.Packages)]
public IEnumerable GetAllPropertyEditors()
{
diff --git a/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs b/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs
index a9f448d575..03e4ad163d 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs
@@ -78,7 +78,7 @@ namespace Umbraco.Web.BackOffice.Controllers
///
/// Deletes a language with a given ID
///
- [TypeFilter(typeof(UmbracoTreeAuthorizeAttribute), Arguments = new object[]{new string[]{ Constants.Trees.Languages} })]
+ [UmbracoTreeAuthorizeAttribute(Constants.Trees.Languages)]
[HttpDelete]
[HttpPost]
public IActionResult DeleteLanguage(int id)
@@ -107,7 +107,7 @@ namespace Umbraco.Web.BackOffice.Controllers
///
/// Creates or saves a language
///
- [TypeFilter(typeof(UmbracoTreeAuthorizeAttribute), Arguments = new object[]{new string[]{ Constants.Trees.Languages} })]
+ [UmbracoTreeAuthorizeAttribute(Constants.Trees.Languages)]
[HttpPost]
public Language SaveLanguage(Language language)
{
diff --git a/src/Umbraco.Web.BackOffice/Controllers/LogController.cs b/src/Umbraco.Web.BackOffice/Controllers/LogController.cs
index 76f7f35838..9bc2be8a39 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/LogController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/LogController.cs
@@ -58,7 +58,7 @@ namespace Umbraco.Web.BackOffice.Controllers
_sqlContext = sqlContext ?? throw new ArgumentNullException(nameof(sqlContext));
}
- [TypeFilter(typeof(UmbracoApplicationAuthorizeAttribute), Arguments = new object[]{new string[]{ Constants.Applications.Content, Constants.Applications.Media } })]
+ [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Content, Constants.Applications.Media)]
public PagedResult GetPagedEntityLog(int id,
int pageNumber = 1,
int pageSize = 10,
diff --git a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs
index 220d67f794..23ba2e5771 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs
@@ -24,7 +24,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// A controller used for managing packages in the back office
///
[PluginController("UmbracoApi")]
- [TypeFilter(typeof(UmbracoApplicationAuthorizeAttribute), Arguments = new object[]{new string[]{Constants.Applications.Packages}})]
+ [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Packages)]
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 9f86d3e5cc..5330d4466f 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs
@@ -30,7 +30,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("UmbracoApi")]
- [TypeFilter(typeof(UmbracoApplicationAuthorizeAttribute), Arguments = new object[]{new string[]{Constants.Applications.Packages} })]
+ [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Packages)]
public class PackageInstallController : UmbracoAuthorizedJsonController
{
diff --git a/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs b/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs
index 699c06f1d9..686afe284b 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs
@@ -18,7 +18,7 @@ using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.BackOffice.Controllers
{
[PluginController("UmbracoApi")]
- [TypeFilter(typeof(UmbracoApplicationAuthorizeAttribute), Arguments = new object[]{new string[]{Constants.Applications.Content} })]
+ [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Content)]
public class RelationController : UmbracoAuthorizedJsonController
{
private readonly UmbracoMapper _umbracoMapper;
diff --git a/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs
index fe43944779..f679dd6b8e 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs
@@ -23,7 +23,7 @@ namespace Umbraco.Web.BackOffice.Controllers
/// The API controller for editing relation types.
///
[PluginController("UmbracoApi")]
- [TypeFilter(typeof(UmbracoTreeAuthorizeAttribute), Arguments = new object[]{new string[]{Constants.Trees.RelationTypes} })]
+ [UmbracoTreeAuthorizeAttribute(Constants.Trees.RelationTypes)]
public class RelationTypeController : BackOfficeNotificationsController
{
private readonly ILogger _logger;
diff --git a/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs b/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs
index dd51126dc5..6eae71a27e 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/TemplateController.cs
@@ -17,7 +17,7 @@ using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.Editors
{
[PluginController("UmbracoApi")]
- [TypeFilter(typeof(UmbracoTreeAuthorizeAttribute), Arguments = new object[]{new string[]{Constants.Trees.Templates} })]
+ [UmbracoTreeAuthorizeAttribute(Constants.Trees.Templates)]
public class TemplateController : BackOfficeNotificationsController
{
private readonly IFileService _fileService;
diff --git a/src/Umbraco.Web.BackOffice/Filters/UmbracoApplicationAuthorizeAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/UmbracoApplicationAuthorizeAttribute.cs
index 2a9f88c0be..81e61af5bf 100644
--- a/src/Umbraco.Web.BackOffice/Filters/UmbracoApplicationAuthorizeAttribute.cs
+++ b/src/Umbraco.Web.BackOffice/Filters/UmbracoApplicationAuthorizeAttribute.cs
@@ -7,50 +7,62 @@ using Umbraco.Core;
namespace Umbraco.Web.BackOffice.Filters
{
- public class UmbracoApplicationAuthorizeAttribute : Attribute, IAuthorizationFilter
+ public class UmbracoApplicationAuthorizeAttribute : TypeFilterAttribute
{
- ///
- /// Can be used by unit tests to enable/disable this filter
- ///
- internal static bool Enable = true;
-
- private readonly IUmbracoContextAccessor _umbracoContextAccessor;
- 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 UmbracoApplicationAuthorizeAttribute(IUmbracoContextAccessor umbracoContextAccessor, params string[] appName)
+ public UmbracoApplicationAuthorizeAttribute(params string[] appName) : base(typeof(UmbracoApplicationAuthorizeFilter))
{
- _umbracoContextAccessor = umbracoContextAccessor;
- _appNames = appName;
+ base.Arguments = new object[]
+ {
+ appName
+ };
}
-
- public void OnAuthorization(AuthorizationFilterContext context)
+ private class UmbracoApplicationAuthorizeFilter : IAuthorizationFilter
{
- if (!IsAuthorized())
- {
- context.Result = new ForbidResult();
- }
- }
+ ///
+ /// Can be used by unit tests to enable/disable this filter
+ ///
+ internal static bool Enable = true;
- private bool IsAuthorized()
- {
- if (Enable == false)
+ private readonly IUmbracoContextAccessor _umbracoContextAccessor;
+ 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(IUmbracoContextAccessor umbracoContextAccessor, params string[] appName)
{
- return true;
+ _umbracoContextAccessor = umbracoContextAccessor;
+ _appNames = appName;
}
- var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext();
- var authorized = umbracoContext.Security.CurrentUser != null
- && _appNames.Any(app => umbracoContext.Security.UserHasSectionAccess(
- app, umbracoContext.Security.CurrentUser));
- return authorized;
+ public void OnAuthorization(AuthorizationFilterContext context)
+ {
+ if (!IsAuthorized())
+ {
+ context.Result = new ForbidResult();
+ }
+ }
+
+ private bool IsAuthorized()
+ {
+ if (Enable == false)
+ {
+ return true;
+ }
+
+ var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext();
+ var authorized = umbracoContext.Security.CurrentUser != null
+ && _appNames.Any(app => umbracoContext.Security.UserHasSectionAccess(
+ app, umbracoContext.Security.CurrentUser));
+
+ return authorized;
+ }
}
}
+
}
diff --git a/src/Umbraco.Web.BackOffice/Filters/UmbracoTreeAuthorizeAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/UmbracoTreeAuthorizeAttribute.cs
index 9a8239b637..6db37d16f6 100644
--- a/src/Umbraco.Web.BackOffice/Filters/UmbracoTreeAuthorizeAttribute.cs
+++ b/src/Umbraco.Web.BackOffice/Filters/UmbracoTreeAuthorizeAttribute.cs
@@ -6,13 +6,24 @@ using Umbraco.Web.Services;
namespace Umbraco.Web.BackOffice.Filters
{
- ///
+
+ public class UmbracoTreeAuthorizeAttribute : TypeFilterAttribute
+ {
+ public UmbracoTreeAuthorizeAttribute(params string[] treeAliases) : base(typeof(UmbracoTreeAuthorizeFilter))
+ {
+ base.Arguments = new object[]
+ {
+ treeAliases
+ };
+ }
+
+ ///
/// Ensures that the current user has access to the application for which the specified tree(s) belongs
///
///
/// This would allow a tree to be moved between sections
///
- public sealed class UmbracoTreeAuthorizeAttribute : IAuthorizationFilter
+ private sealed class UmbracoTreeAuthorizeFilter : IAuthorizationFilter
{
///
/// Can be used by unit tests to enable/disable this filter
@@ -32,7 +43,7 @@ namespace Umbraco.Web.BackOffice.Filters
/// Multiple trees may be specified.
///
///
- public UmbracoTreeAuthorizeAttribute(ITreeService treeService, IUmbracoContextAccessor umbracoContextAccessor, params string[] treeAliases)
+ public UmbracoTreeAuthorizeFilter(ITreeService treeService, IUmbracoContextAccessor umbracoContextAccessor, params string[] treeAliases)
{
_treeService = treeService;
_umbracoContextAccessor = umbracoContextAccessor;
@@ -68,4 +79,7 @@ namespace Umbraco.Web.BackOffice.Filters
}
}
}
+ }
+
+
}
diff --git a/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs b/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs
index 00de2db0b6..131a4ac62c 100644
--- a/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs
+++ b/src/Umbraco.Web.BackOffice/HealthCheck/HealthCheckController.cs
@@ -13,7 +13,7 @@ namespace Umbraco.Web.HealthCheck
///
/// The API controller used to display the health check info and execute any actions
///
- [TypeFilter(typeof(UmbracoApplicationAuthorizeAttribute), Arguments = new object[]{new string []{Constants.Applications.Settings}})]
+ [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Settings)]
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 ba55dcb51c..b6cdf75f6f 100644
--- a/src/Umbraco.Web.BackOffice/Profiling/WebProfilingController.cs
+++ b/src/Umbraco.Web.BackOffice/Profiling/WebProfilingController.cs
@@ -10,8 +10,8 @@ namespace Umbraco.Web.BackOffice.Profiling
///
/// The API controller used to display the state of the web profiler
///
- [TypeFilter(typeof(UmbracoApplicationAuthorizeAttribute), Arguments = new object[]{new string []{Constants.Applications.Settings}})]
- public class WebProfilingController : UmbracoAuthorizedJsonController
+ [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Settings)]
+ public class WebProfilingController : UmbracoAuthorizedJsonController
{
private readonly IHostingEnvironment _hosting;
diff --git a/src/Umbraco.Web.BackOffice/Validation/PrefixlessBodyModelValidator.cs b/src/Umbraco.Web.BackOffice/Validation/PrefixlessBodyModelValidator.cs
new file mode 100644
index 0000000000..d22b044e51
--- /dev/null
+++ b/src/Umbraco.Web.BackOffice/Validation/PrefixlessBodyModelValidator.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
+
+namespace Umbraco.Web.BackOffice.Validation
+{
+ public class PrefixlessBodyModelValidator : ObjectModelValidator
+ {
+ public PrefixlessBodyModelValidator(IModelMetadataProvider modelMetadataProvider, IList validatorProviders) :
+ base(modelMetadataProvider, validatorProviders)
+ {
+
+ }
+
+ public override ValidationVisitor GetValidationVisitor(ActionContext actionContext, IModelValidatorProvider validatorProvider,
+ ValidatorCache validatorCache, IModelMetadataProvider metadataProvider, ValidationStateDictionary validationState)
+ {
+ var visitor = new PrefixlessValidationVisitor(
+ actionContext,
+ validatorProvider,
+ validatorCache,
+ metadataProvider,
+ validationState);
+
+ return visitor;
+ }
+
+ private class PrefixlessValidationVisitor : ValidationVisitor
+ {
+ public PrefixlessValidationVisitor(ActionContext actionContext, IModelValidatorProvider validatorProvider, ValidatorCache validatorCache, IModelMetadataProvider metadataProvider, ValidationStateDictionary validationState)
+ : base(actionContext, validatorProvider, validatorCache, metadataProvider, validationState) {
+
+ }
+
+ public override bool Validate(ModelMetadata metadata, string key, object model, bool alwaysValidateAtTopLevel)
+ {
+ return base.Validate(metadata, string.Empty, model, alwaysValidateAtTopLevel);
+ }
+ }
+ }
+
+
+}