Remove property value permissions when related content and/or property types are removed (#19778)

* Removed two unnecessary delete clauses when removing content types (they are looking for user group Ids, but we are deleting a content type).

* Renamed table name constant with obsoletion to better reflect name and contents of table.

* Added granular permission for property value records to delete clauses when deleting a document type.

* Delete property value permissions for removed property types.

* Added integration tests to verify behaviour.
This commit is contained in:
Andy Butland
2025-08-07 14:22:19 +02:00
committed by GitHub
parent 8a94383262
commit a0aff9d10c
7 changed files with 124 additions and 24 deletions

View File

@@ -1,19 +1,19 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using NUnit.Framework;
using Umbraco.Cms.Api.Management.Mapping.Permissions;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Models.Membership.Permissions;
using Umbraco.Cms.Core.Persistence;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.Services;
@@ -52,8 +52,11 @@ internal sealed class ContentTypeRepositoryTest : UmbracoIntegrationTest
private IMediaTypeRepository MediaTypeRepository => GetRequiredService<IMediaTypeRepository>();
private IDocumentRepository DocumentRepository => GetRequiredService<IDocumentRepository>();
private IContentService ContentService => GetRequiredService<IContentService>();
private IUserGroupRepository UserGroupRepository => GetRequiredService<IUserGroupRepository>();
private ContentTypeRepository ContentTypeRepository =>
(ContentTypeRepository)GetRequiredService<IContentTypeRepository>();
@@ -918,4 +921,83 @@ internal sealed class ContentTypeRepositoryTest : UmbracoIntegrationTest
Assert.That(hasCulture, Is.True);
}
}
[Test]
public void Can_Remove_Property_Value_Permissions_On_Removal_Of_Property_Types()
{
var provider = ScopeProvider;
using (var scope = provider.CreateScope())
{
// Create, save and re-retrieve a content type and user group.
IContentType contentType = ContentTypeBuilder.CreateSimpleContentType(defaultTemplateId: 0);
ContentTypeRepository.Save(contentType);
contentType = ContentTypeRepository.Get(contentType.Id);
var userGroup = CreateUserGroupWithGranularPermissions(contentType);
// Remove property types and verify that the permission is removed from the user group.
contentType.RemovePropertyType("author");
ContentTypeRepository.Save(contentType);
userGroup = UserGroupRepository.Get(userGroup.Id);
Assert.AreEqual(3, userGroup.GranularPermissions.Count);
contentType.RemovePropertyType("bodyText");
ContentTypeRepository.Save(contentType);
userGroup = UserGroupRepository.Get(userGroup.Id);
Assert.AreEqual(2, userGroup.GranularPermissions.Count);
contentType.RemovePropertyType("title");
ContentTypeRepository.Save(contentType);
userGroup = UserGroupRepository.Get(userGroup.Id);
Assert.AreEqual(0, userGroup.GranularPermissions.Count);
}
}
[Test]
public void Can_Remove_Property_Value_Permissions_On_Removal_Of_Content_Type()
{
var provider = ScopeProvider;
using (var scope = provider.CreateScope())
{
// Create, save and re-retrieve a content type and user group.
IContentType contentType = ContentTypeBuilder.CreateSimpleContentType(defaultTemplateId: 0);
ContentTypeRepository.Save(contentType);
contentType = ContentTypeRepository.Get(contentType.Id);
var userGroup = CreateUserGroupWithGranularPermissions(contentType);
// Remove the content type and verify all permissions are removed from the user group.
ContentTypeRepository.Delete(contentType);
userGroup = UserGroupRepository.Get(userGroup.Id);
Assert.AreEqual(0, userGroup.GranularPermissions.Count);
}
}
private IUserGroup CreateUserGroupWithGranularPermissions(IContentType contentType)
{
DocumentPropertyValueGranularPermission CreatePermission(IPropertyType propertyType, string permission = "")
=> new()
{
Key = contentType.Key,
Permission = propertyType.Key.ToString().ToLowerInvariant() + "|" + permission,
};
var titlePropertyType = contentType.PropertyTypes.Single(x => x.Alias == "title");
var bodyTextPropertyType = contentType.PropertyTypes.Single(x => x.Alias == "bodyText");
var authorPropertyType = contentType.PropertyTypes.Single(x => x.Alias == "author");
var userGroup = new UserGroupBuilder()
.WithGranularPermissions([
CreatePermission(titlePropertyType, "Umb.Document.PropertyValue.Read"),
CreatePermission(titlePropertyType, "Umb.Document.PropertyValue.Write"),
CreatePermission(bodyTextPropertyType, "Umb.Document.PropertyValue.Read"),
CreatePermission(authorPropertyType)
])
.Build();
UserGroupRepository.Save(userGroup);
userGroup = UserGroupRepository.Get(userGroup.Id);
Assert.AreEqual(4, userGroup.GranularPermissions.Count);
return userGroup;
}
}