From e34b2c09e0942b35fb0ab22c2ab708a61424ef09 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 19 Dec 2023 14:18:48 +0100 Subject: [PATCH 1/2] Add policies for content template handling (#15482) --- .../Controllers/ContentController.cs | 5 ++++- .../UmbracoBuilder.BackOfficeAuth.cs | 7 +++++++ .../Filters/ContentSaveValidationAttribute.cs | 14 ++++++++++---- .../Authorization/AuthorizationPolicies.cs | 1 + 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs index d06062498a..6ba81172c8 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs @@ -826,6 +826,7 @@ public class ContentController : ContentControllerBase /// The content id to copy /// The name of the blueprint /// + [Authorize(Policy = AuthorizationPolicies.ContentPermissionCreateBlueprintFromId)] [HttpPost] public ActionResult CreateBlueprintFromContent( [FromQuery] int contentId, @@ -881,8 +882,9 @@ public class ContentController : ContentControllerBase /// /// Saves content /// + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] [FileUploadCleanupFilter] - [ContentSaveValidation] + [ContentSaveValidation(skipUserAccessValidation:true)] // skip user access validation because we "only" require Settings access to create new blueprints from scratch public async Task?>?> PostSaveBlueprint( [ModelBinder(typeof(BlueprintItemBinder))] ContentItemSave contentItem) { @@ -2077,6 +2079,7 @@ public class ContentController : ContentControllerBase return Ok(); } + [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] [HttpDelete] [HttpPost] public IActionResult DeleteBlueprint(int id) diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeAuth.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeAuth.cs index bfd460bda4..0945d3459b 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeAuth.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeAuth.cs @@ -179,6 +179,13 @@ public static partial class UmbracoBuilderExtensions policy.Requirements.Add(new ContentPermissionsQueryStringRequirement(ActionDelete.ActionLetter)); }); + options.AddPolicy(AuthorizationPolicies.ContentPermissionCreateBlueprintFromId, policy => + { + policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); + policy.Requirements.Add( + new ContentPermissionsQueryStringRequirement(ActionCreateBlueprintFromContent.ActionLetter, "contentId")); + }); + options.AddPolicy(AuthorizationPolicies.BackOfficeAccess, policy => { policy.AuthenticationSchemes.Add(backOfficeAuthenticationScheme); diff --git a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs index 100d089451..f7be9d129a 100644 --- a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs @@ -20,9 +20,12 @@ namespace Umbraco.Cms.Web.BackOffice.Filters; /// internal sealed class ContentSaveValidationAttribute : TypeFilterAttribute { - public ContentSaveValidationAttribute() : base(typeof(ContentSaveValidationFilter)) => + public ContentSaveValidationAttribute(bool skipUserAccessValidation = false) + : base(typeof(ContentSaveValidationFilter)) + { Order = -3000; // More important than ModelStateInvalidFilter.FilterOrder - + Arguments = new object[] { skipUserAccessValidation }; + } private sealed class ContentSaveValidationFilter : IAsyncActionFilter { @@ -32,6 +35,7 @@ internal sealed class ContentSaveValidationAttribute : TypeFilterAttribute private readonly ILocalizationService _localizationService; private readonly ILoggerFactory _loggerFactory; private readonly IPropertyValidationService _propertyValidationService; + private readonly bool _skipUserAccessValidation; public ContentSaveValidationFilter( @@ -40,7 +44,8 @@ internal sealed class ContentSaveValidationAttribute : TypeFilterAttribute IPropertyValidationService propertyValidationService, IAuthorizationService authorizationService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, - ILocalizationService localizationService) + ILocalizationService localizationService, + bool skipUserAccessValidation) { _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); @@ -49,6 +54,7 @@ internal sealed class ContentSaveValidationAttribute : TypeFilterAttribute _authorizationService = authorizationService; _backOfficeSecurityAccessor = backOfficeSecurityAccessor; _localizationService = localizationService; + _skipUserAccessValidation = skipUserAccessValidation; } public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) @@ -88,7 +94,7 @@ internal sealed class ContentSaveValidationAttribute : TypeFilterAttribute return; } - if (!await ValidateUserAccessAsync(model, context)) + if (_skipUserAccessValidation is false && await ValidateUserAccessAsync(model, context) is false) { return; } diff --git a/src/Umbraco.Web.Common/Authorization/AuthorizationPolicies.cs b/src/Umbraco.Web.Common/Authorization/AuthorizationPolicies.cs index cfad020a92..429cc77faf 100644 --- a/src/Umbraco.Web.Common/Authorization/AuthorizationPolicies.cs +++ b/src/Umbraco.Web.Common/Authorization/AuthorizationPolicies.cs @@ -22,6 +22,7 @@ public static class AuthorizationPolicies public const string ContentPermissionProtectById = nameof(ContentPermissionProtectById); public const string ContentPermissionBrowseById = nameof(ContentPermissionBrowseById); public const string ContentPermissionDeleteById = nameof(ContentPermissionDeleteById); + public const string ContentPermissionCreateBlueprintFromId = nameof(ContentPermissionCreateBlueprintFromId); public const string MediaPermissionByResource = nameof(MediaPermissionByResource); public const string MediaPermissionPathById = nameof(MediaPermissionPathById); From 11cef81c77ccbade8c967091653d8dcab90c4f85 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 19 Dec 2023 14:21:00 +0100 Subject: [PATCH 2/2] Bump version --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 26ecc33103..a04a865bbf 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", - "version": "13.0.1", + "version": "13.0.2", "assemblyVersion": { "precision": "build" },