Merge branch 'v15/dev' into v16/merge-from-15

# Conflicts:
#	src/Umbraco.Cms.Api.Management/Controllers/PublishedCache/RebuildPublishedCacheController.cs
#	src/Umbraco.Cms.Api.Management/Factories/UserPresentationFactory.cs
#	src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs
#	src/Umbraco.Core/Services/ContentEditingService.cs
#	src/Umbraco.Core/Services/DataTypeService.cs
#	src/Umbraco.Core/Services/IContentEditingService.cs
#	src/Umbraco.Core/Services/IDataTypeService.cs
#	src/Umbraco.Core/Services/ITrackedReferencesService.cs
#	src/Umbraco.Core/Services/RelationService.cs
#	src/Umbraco.Core/Services/TrackedReferencesService.cs
#	src/Umbraco.Infrastructure/Examine/Deferred/DeliveryApiContentIndexHandleContentTypeChanges.cs
#	src/Umbraco.Infrastructure/Examine/DeliveryApiIndexingHandler.cs
#	src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs
#	src/Umbraco.Web.UI.Client/src/external/backend-api/src/sdk.gen.ts
#	src/Umbraco.Web.UI.Client/src/mocks/data/document-blueprint/document-blueprint.data.ts
#	src/Umbraco.Web.UI.Client/src/mocks/data/document/document.db.ts
#	src/Umbraco.Web.UI.Client/src/packages/core/router/modal-registration/modal-route-registration.controller.ts
#	src/Umbraco.Web.UI.Client/src/packages/core/router/route/route.context.ts
#	src/Umbraco.Web.UI.Client/src/packages/core/router/route/route.interface.ts
#	src/Umbraco.Web.UI.Client/src/packages/core/router/route/router-slot.element.ts
#	src/Umbraco.Web.UI.Client/src/packages/core/router/router-slot/model.ts
#	src/Umbraco.Web.UI.Client/src/packages/data-type/reference/repository/data-type-reference.server.data.ts
#	src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/repository/document-publishing.server.data-source.ts
#	src/Umbraco.Web.UI.Client/src/packages/documents/documents/rollback/entity-action/rollback.action.ts
#	tests/Umbraco.Tests.AcceptanceTest/package-lock.json
#	tests/Umbraco.Tests.AcceptanceTest/package.json
#	tests/Umbraco.Tests.Common/Builders/UserGroupBuilder.cs
#	tests/Umbraco.Tests.Integration/Umbraco.Core/Services/TemporaryFileServiceTests.cs
This commit is contained in:
Andy Butland
2025-04-09 22:05:59 +02:00
91 changed files with 2817 additions and 368 deletions

View File

@@ -34,15 +34,15 @@ internal sealed class UserGroupPresentationFactoryTests : UmbracoIntegrationTest
services.AddTransient<IUserGroupPresentationFactory, UserGroupPresentationFactory>();
services.AddSingleton<IPermissionPresentationFactory, PermissionPresentationFactory>();
services.AddSingleton<DocumentPermissionMapper>();
services.AddSingleton<IPermissionMapper>(x=>x.GetRequiredService<DocumentPermissionMapper>());
services.AddSingleton<IPermissionPresentationMapper>(x=>x.GetRequiredService<DocumentPermissionMapper>());
services.AddSingleton<IPermissionMapper>(x => x.GetRequiredService<DocumentPermissionMapper>());
services.AddSingleton<IPermissionPresentationMapper>(x => x.GetRequiredService<DocumentPermissionMapper>());
}
[Test]
public async Task Can_Map_Create_Model_And_Create()
{
var updateModel = new CreateUserGroupRequestModel()
var createModel = new CreateUserGroupRequestModel()
{
Alias = "testAlias",
FallbackPermissions = new HashSet<string>(),
@@ -53,7 +53,7 @@ internal sealed class UserGroupPresentationFactoryTests : UmbracoIntegrationTest
Permissions = new HashSet<IPermissionPresentationModel>()
};
var attempt = await UserGroupPresentationFactory.CreateAsync(updateModel);
var attempt = await UserGroupPresentationFactory.CreateAsync(createModel);
Assert.IsTrue(attempt.Success);
var userGroupCreateAttempt = await UserGroupService.CreateAsync(attempt.Result, Constants.Security.SuperUserKey);
@@ -71,7 +71,7 @@ internal sealed class UserGroupPresentationFactoryTests : UmbracoIntegrationTest
[Test]
public async Task Cannot_Create_UserGroup_With_Unexisting_Document_Reference()
{
var updateModel = new CreateUserGroupRequestModel()
var createModel = new CreateUserGroupRequestModel()
{
Alias = "testAlias",
FallbackPermissions = new HashSet<string>(),
@@ -89,7 +89,7 @@ internal sealed class UserGroupPresentationFactoryTests : UmbracoIntegrationTest
}
};
var attempt = await UserGroupPresentationFactory.CreateAsync(updateModel);
var attempt = await UserGroupPresentationFactory.CreateAsync(createModel);
Assert.IsTrue(attempt.Success);
var userGroupCreateAttempt = await UserGroupService.CreateAsync(attempt.Result, Constants.Security.SuperUserKey);
@@ -102,11 +102,11 @@ internal sealed class UserGroupPresentationFactoryTests : UmbracoIntegrationTest
}
[Test]
public async Task Can_Create_Usergroup_With_Empty_Granluar_Permissions_For_Document()
public async Task Can_Create_Usergroup_With_Empty_Granular_Permissions_For_Document()
{
var contentKey = await CreateContent();
var updateModel = new CreateUserGroupRequestModel()
var createModel = new CreateUserGroupRequestModel()
{
Alias = "testAlias",
FallbackPermissions = new HashSet<string>(),
@@ -124,7 +124,7 @@ internal sealed class UserGroupPresentationFactoryTests : UmbracoIntegrationTest
}
};
var attempt = await UserGroupPresentationFactory.CreateAsync(updateModel);
var attempt = await UserGroupPresentationFactory.CreateAsync(createModel);
Assert.IsTrue(attempt.Success);
var userGroupCreateAttempt = await UserGroupService.CreateAsync(attempt.Result, Constants.Security.SuperUserKey);

View File

@@ -0,0 +1,191 @@
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Mapping.Permissions;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.ViewModels.UserGroup.Permissions;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Models.Membership.Permissions;
using Umbraco.Cms.Core.Routing;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Infrastructure.Persistence.Mappers;
using Umbraco.Cms.Tests.Common.Builders;
using Umbraco.Cms.Tests.Common.Builders.Extensions;
using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Cms.Tests.Integration.Testing;
namespace Umbraco.Cms.Tests.Integration.ManagementApi.Factories;
[TestFixture]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
public class UserPresentationFactoryTests : UmbracoIntegrationTestWithContent
{
public IUserPresentationFactory UserPresentationFactory => GetRequiredService<IUserPresentationFactory>();
public IUserGroupService UserGroupService => GetRequiredService<IUserGroupService>();
public IUserService UserService => GetRequiredService<IUserService>();
public ILanguageService LanguageService => GetRequiredService<ILanguageService>();
public IMediaService MediaService => GetRequiredService<IMediaService>();
protected override void ConfigureTestServices(IServiceCollection services)
{
services.AddTransient<IUserPresentationFactory, UserPresentationFactory>();
services.AddTransient<IUserGroupPresentationFactory, UserGroupPresentationFactory>();
services.AddSingleton<IAbsoluteUrlBuilder, DefaultAbsoluteUrlBuilder>();
services.AddSingleton<IUrlAssembler, DefaultUrlAssembler>();
services.AddSingleton<IPasswordConfigurationPresentationFactory, PasswordConfigurationPresentationFactory>();
services.AddSingleton<IPermissionPresentationFactory, PermissionPresentationFactory>();
services.AddSingleton<DocumentPermissionMapper>();
services.AddSingleton<IPermissionMapper>(x => x.GetRequiredService<DocumentPermissionMapper>());
services.AddSingleton<IPermissionPresentationMapper>(x => x.GetRequiredService<DocumentPermissionMapper>());
}
[Test]
public async Task Can_Create_Current_User_Response_Model()
{
var daLanguage = new LanguageBuilder()
.WithCultureInfo("da-DK")
.Build();
await LanguageService.CreateAsync(daLanguage, Constants.Security.SuperUserKey);
var enUsLanguage = await LanguageService.GetAsync("en-US");
var daDkLanguage = await LanguageService.GetAsync("da-DK");
var rootContentKey = Guid.Parse(TextpageKey);
var subPageContentKey = Guid.Parse(SubPageKey);
var subPage2ContentKey = Guid.Parse(SubPage2Key);
var rootMediaFolder = MediaService.CreateMedia("Pictures Folder", Constants.System.Root, "Folder");
MediaService.Save(rootMediaFolder);
var groupOne = await CreateUserGroup(
"Group One",
"groupOne",
[enUsLanguage.Id],
["A", "B", "C"],
[
new DocumentGranularPermission
{
Key = rootContentKey,
Permission = "A",
},
new DocumentGranularPermission
{
Key = rootContentKey,
Permission = "E",
},
new DocumentGranularPermission
{
Key = subPageContentKey,
Permission = "F",
},
new DocumentGranularPermission
{
Key = subPage2ContentKey,
Permission = "F",
}
],
rootMediaFolder.Id);
var groupTwo = await CreateUserGroup(
"Group Two",
"groupTwo",
[daDkLanguage.Id],
["A", "B", "D"],
[
new DocumentGranularPermission
{
Key = subPage2ContentKey,
Permission = "G",
},
new DocumentGranularPermission
{
Key = subPage2ContentKey,
Permission = "H",
}
],
rootMediaFolder.Id);
var user = await CreateUser([groupOne.Key, groupTwo.Key]);
var model = await UserPresentationFactory.CreateCurrentUserResponseModelAsync(user);
Assert.AreEqual(user.Key, model.Id);
Assert.AreEqual("test@test.com", model.Email);
Assert.AreEqual("Test User", model.Name);
Assert.AreEqual("test@test.com", model.UserName);
Assert.AreEqual(2, model.UserGroupIds.Count);
Assert.IsTrue(model.UserGroupIds.Select(x => x.Id).ContainsAll([groupOne.Key, groupTwo.Key]));
Assert.IsFalse(model.HasAccessToAllLanguages);
Assert.AreEqual(2, model.Languages.Count());
Assert.IsTrue(model.Languages.ContainsAll(["en-US", "da-DK"]));
Assert.IsTrue(model.HasDocumentRootAccess);
Assert.AreEqual(0, model.DocumentStartNodeIds.Count);
Assert.IsFalse(model.HasMediaRootAccess);
Assert.AreEqual(1, model.MediaStartNodeIds.Count);
Assert.AreEqual(rootMediaFolder.Key, model.MediaStartNodeIds.First().Id);
Assert.IsFalse(model.HasAccessToSensitiveData);
Assert.AreEqual(4, model.FallbackPermissions.Count);
Assert.IsTrue(model.FallbackPermissions.ContainsAll(["A", "B", "C", "D"]));
// When aggregated, we expect one permission per document (we have several granular permissions assigned, for three unique documents).
Assert.AreEqual(3, model.Permissions.Count);
// User has two user groups, one of which provides granular permissions for the root content item.
// As such we expect the aggregated permissions to be the union of the specific permissions coming from the user group with them assigned to the document,
// and the fallback permissions from the other.
var rootContentPermissions = model.Permissions.Cast<DocumentPermissionPresentationModel>().Single(x => x.Document.Id == rootContentKey);
Assert.AreEqual(4, rootContentPermissions.Verbs.Count);
Assert.IsTrue(rootContentPermissions.Verbs.ContainsAll(["A", "B", "D", "E"]));
// The sub-page and it's parent have specific granular permissions from one user group.
// So we expect the aggregated permissions to include those from the sub-page and the other user's groups fallback permissions.
var subPageContentPermissions = model.Permissions.Cast<DocumentPermissionPresentationModel>().Single(x => x.Document.Id == subPageContentKey);
Assert.AreEqual(4, subPageContentPermissions.Verbs.Count);
Assert.IsTrue(subPageContentPermissions.Verbs.ContainsAll(["A", "B", "D", "F"]));
// Both user groups provide granular permissions for the second sub-page content item.
// Here we expect the aggregated permissions to be the union of the granular permissions on the document from both user groups.
var subPage2ContentPermissions = model.Permissions.Cast<DocumentPermissionPresentationModel>().Single(x => x.Document.Id == subPage2ContentKey);
Assert.AreEqual(3, subPage2ContentPermissions.Verbs.Count);
Assert.IsTrue(subPage2ContentPermissions.Verbs.ContainsAll(["F", "G", "H"]));
}
private async Task<IUserGroup> CreateUserGroup(
string name,
string alias,
int[] allowedLanguages,
string[] permissions,
DocumentGranularPermission[] granularPermissions,
int startMediaId)
{
var userGroup = new UserGroupBuilder()
.WithName(name)
.WithAlias(alias)
.WithAllowedLanguages(allowedLanguages)
.WithStartMediaId(startMediaId)
.WithPermissions(permissions.ToHashSet())
.WithGranularPermissions(granularPermissions)
.Build();
var createUserGroupResult = await UserGroupService.CreateAsync(userGroup, Constants.Security.SuperUserKey);
Assert.IsTrue(createUserGroupResult.Success);
return userGroup;
}
private async Task<IUser> CreateUser(Guid[] userGroupKeys)
{
var createUserAttempt = await UserService.CreateAsync(Constants.Security.SuperUserKey, new UserCreateModel
{
Email = "test@test.com",
Name = "Test User",
UserName = "test@test.com",
UserGroupKeys = userGroupKeys.ToHashSet(),
});
Assert.IsTrue(createUserAttempt.Success);
return await UserService.GetAsync(createUserAttempt.Result.CreatedUser.Key);
}
}