Files
Umbraco-CMS/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/UserServiceCrudTests.Update.cs
Andy Butland 825f791d01 Remove the non-controversial, straightforward obsoleted constructs for Umbraco 16 (#18661)
* Removed obsoletes from IConfigManipulator.

* Removed obsolete models builder extensions.

* Removed the obsolete ContentDashboardSettings.

* Removed the obsolete InstallMissingDatabase setting on GlobalSettings.

* Removed obsolete NuCache settings.

* Removed obsolete RuntimeMinificationSettings.

* Removed obsolete health check constant.

* Removed obsolete icon constant.

* Removed obsolete telemetry constant.

* Removed obsolete property and constructor on UmbracoBuilder.

* Removed obsolete constructor on AuditNotificationsHandler.

* Removed obsolete constructor on HTTP header health checks.

* Removed obsolete constructor on MediaFileManager.

* Removed obsolete GetDefaultFileContent on ViewHelper.

* Remove obsoleted methods on embed providers.

* Fix tests.

* Removed obsolete constructors on BlockEditorDataConverter.

* Removed obsolete SeedCacheDuration property on CacheSettings.

* Removed obsolete PublishCulture on ContentRepositoryExtensions.

* Removed obsolete MonitorLock.

* Removed obsolete synchronous HasSavedValues from IDataTypeUsageService and IDataTypeUsageRepository.

* Removed obsolete HasSavedPropertyValues from IPropertyTypeUsageService and IPropertyTypeUsageRepository.

* Removed obsolete methods in ITrackedReferencesService and ITrackedReferencesRepository.

* Removed obsolete DateValueEditor constructors.

* Removed obsolete GetAutomaticRelationTypesAliases.

* Removed obsolete constructor on TextOnlyValueEditor.

* Removed obsolete constructors on RegexValidator and RequiredValidator.

* Removed obsolete constructs on SliderValueConverter and TagsValueConverter.

* Removed obsolete GetContentType methods from IPublishedCache.

* Removed ContentFinderByIdPath.

* Removed obsolete constructor on DefaultMediaUrlProvider.

* Removed obsolete constructor on Domain.

* Removed obsolete constructor on PublishedRequest.

* Removed obsolete methods on CheckPermissions.

* Removed obsolete GetUserId from IBackOfficeSecurity.

* Removed obsolete methods on LegacyPasswordSecurity.

* Removed obsolete constructors on AuditService.

* Removed obsolete methods on IContentEditingService.

* Remove obsolete constructors and methods on ContentService/IContentService.

* Removed obsolete constructor in ContentTypeEditingService.

* Removed obsolete constructor in MediaTypeEditingService.

* Removed obsolete constructor in MemberTypeEditingService.

* Removed obsolete constructor in ContentTypeService.

* Removed obsolete constructors in ContentTypeServiceBase.

* Removed obsolete constructors and methods in ContentVersionService.

* Removed obsolete constructor in DataTypeUsageService.

* Removed obsolete constructor in DomainService.

* Removed obsolete constructor in FileService.

* Removes obsolete AttemptMove from IContentService.

* Removes obsolete SetPreventCleanup from IContentVersionService.

* Removes obsolete GetReferences from IDataTypeService.

* Removed obsolete SetConsentLevel from IMetricsConsentService.

* Removed obsolete methods from IPackageDataInstallation.

* Removed obsolete methods from IPackagingService.

* Removed obsolete methods on ITwoFactorLoginService.
Removed obsolete ITemporaryMediaService.

* Removed obsolete constructor from MediaService, MemberTypeService and MediaTypeService.

* More obsolete constructors.

* Removed obsoleted overloads on IPropertyValidationService.

* Fixed build for tests.

* Removed obsolete constructor for PublicAccessService, UserService and RelationService.

* Removed GetDefaultMemberType.

* Removed obsolete user group functionality from IUserService.

* Removed obsolete extension methods on IUserService.

* Removed obsolete method from ITelemetryService.

* Removed obsolete UdiParserServiceConnectors.

* Removed obsolete method on ICookieManager.

* Removed obsolete DynamicContext.

* Removed obsolete XmlHelper.

* Fixed failing integration tests.

* Removed obsoletes in Umbraco.Cms.Api.Common

* Removed obsoletes in Umbraco.Cms.Api.Delivery

* Removed obsoletes in Umbraco.Cms.Api.Management

* Removed obsoletes in Umbraco.Examine.Lucene

* Removed obsoletes in Umbraco.Infrastructure

* Fix failing delivery API contract integration test.

* Made integration tests internal.

* Removed obsoletes from web projects.

* Fix build.

* Removed Twitter OEmbed provider

* Removed obsolete constructor on PublishedDataType.

* Removed obsolete constructors on PublishedCacheBase.

* Removed the obsolete PropertyEditorTagsExtensions.

* Removed obsoletion properties on configuration response  models (#18697)

* Removed obsolete methods from server-side models.

* Update client-side types and sdk.

* Update client-side files.

* Removed obsoletion of Utf8ToAsciiConverter.ToAsciiString overload. (#18694)

* Removed obsolete method in UserService. (#18710)

* Removed obsoleted group alias keys from being publicly available. (#18682)

* Removed unneceessary ApiVersion attribute.

* Clean-up obsoletions on MemberService (#18703)

* Removed obsoleted method on MemberService, added future obsoletion to interface and updated all callers.

* Removed obsoletion on member service method that's not obsolete on the interface.
2025-03-21 17:02:31 +00:00

388 lines
15 KiB
C#

using Moq;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;
internal sealed partial class UserServiceCrudTests
{
private ISet<Guid> GetKeysFromIds(IEnumerable<int>? ids, UmbracoObjectTypes type)
{
IEnumerable<Guid>? keys = ids?
.Select(x => EntityService.GetKey(x, type))
.Where(x => x.Success)
.Select(x => x.Result);
return keys is null
? new HashSet<Guid>()
: new HashSet<Guid>(keys);
}
private async Task<UserUpdateModel> MapUserToUpdateModel(IUser user)
{
var groups = await UserGroupService.GetAsync(user.Groups.Select(x => x.Id).ToArray());
return new UserUpdateModel
{
ExistingUserKey = user.Key,
Email = user.Email,
Name = user.Name,
UserName = user.Username,
LanguageIsoCode = user.Language,
ContentStartNodeKeys = GetKeysFromIds(user.StartContentIds, UmbracoObjectTypes.Document),
MediaStartNodeKeys = GetKeysFromIds(user.StartMediaIds, UmbracoObjectTypes.Media),
UserGroupKeys = groups.Select(x=>x.Key).ToHashSet(),
};
}
private async Task<(UserUpdateModel updateModel, IUser createdUser)> CreateUserForUpdate(
IUserService userService,
string email = "test@test.com",
string userName = "test@test.com")
{
var userGroup = await UserGroupService.GetAsync(Constants.Security.AdminGroupAlias);
var createUserModel = new UserCreateModel
{
Email = email,
UserName = userName,
Name = "Test Mc. Gee",
UserGroupKeys = new HashSet<Guid> { userGroup.Key }
};
var createExistingUser = await userService.CreateAsync(Constants.Security.SuperUserKey, createUserModel, true);
Assert.IsTrue(createExistingUser.Success);
Assert.IsNotNull(createExistingUser.Result.CreatedUser);
var savedUser = createExistingUser.Result.CreatedUser;
var updateModel = await MapUserToUpdateModel(savedUser);
return (updateModel, createExistingUser.Result.CreatedUser);
}
[Test]
[TestCase(true, false)]
[TestCase(false, true)]
public async Task Cannot_Change_Email_When_Deny_Local_Login_Is_True(bool denyLocalLogin, bool shouldSucceed)
{
var localLoginSetting = new Mock<ILocalLoginSettingProvider>();
localLoginSetting.Setup(x => x.HasDenyLocalLogin()).Returns(denyLocalLogin);
var userService = CreateUserService(
localLoginSettingProvider: localLoginSetting.Object,
securitySettings: new SecuritySettings { UsernameIsEmail = false });
var (updateModel, _) = await CreateUserForUpdate(userService);
var updatedEmail = "updated@email.com";
updateModel.Email = updatedEmail;
var result = await userService.UpdateAsync(Constants.Security.SuperUserKey, updateModel);
if (shouldSucceed is false)
{
Assert.IsFalse(result.Success);
Assert.AreEqual(UserOperationStatus.EmailCannotBeChanged, result.Status);
return;
}
Assert.IsTrue(result.Success);
// We'll get the user again to ensure that the changes has been persisted
var updatedUser = await userService.GetAsync(result.Result.Key);
Assert.IsNotNull(updatedUser);
Assert.AreEqual(updatedEmail, updatedUser.Email);
}
[Test]
[TestCase("same@email.com", "same@email.com", true)]
[TestCase("different@email.com", "another@email.com", false)]
[TestCase("notAnEmail", "some@email.com", false)]
public async Task UserName_And_Email_Must_Be_same_When_UserNameIsEmail_Equals_True(string userName, string email, bool shouldSucceed)
{
var userService = CreateUserService(securitySettings: new SecuritySettings { UsernameIsEmail = true });
var (updateModel, createdUser) = await CreateUserForUpdate(userService);
updateModel.UserName = userName;
updateModel.Email = email;
var result = await userService.UpdateAsync(Constants.Security.SuperUserKey, updateModel);
if (shouldSucceed is false)
{
Assert.IsFalse(result.Success);
Assert.AreEqual(UserOperationStatus.UserNameIsNotEmail, result.Status);
return;
}
Assert.IsTrue(result.Success);
var updatedUser = await userService.GetAsync(createdUser.Key);
Assert.IsNotNull(updatedUser);
Assert.AreEqual(userName, updatedUser.Username);
Assert.AreEqual(email, updatedUser.Email);
}
[Test]
public async Task Can_Update_User_Name()
{
const string userName = "UpdateUserName";
const string name = "UpdatedName";
const string email = "update@email.com";
var userService = CreateUserService(securitySettings: new SecuritySettings { UsernameIsEmail = false });
var (updateModel, createdUser) = await CreateUserForUpdate(userService);
updateModel.UserName = userName;
updateModel.Email = email;
updateModel.Name = name;
var result = await userService.UpdateAsync(Constants.Security.SuperUserKey, updateModel);
Assert.IsTrue(result.Success);
var updatedUser = await userService.GetAsync(createdUser.Key);
Assert.Multiple(() =>
{
Assert.IsNotNull(updatedUser);
Assert.AreEqual(userName, updatedUser.Username);
Assert.AreEqual(email, updatedUser.Email);
Assert.AreEqual(name, updatedUser.Name);
});
}
[Test]
public async Task Cannot_Change_Email_To_Duplicate_Email_On_Update()
{
var userService = CreateUserService();
var userGroup = await UserGroupService.GetAsync(Constants.Security.AdminGroupAlias);
var email = "thiswillbe@duplicate.com";
var createModel = new UserCreateModel
{
Email = email,
UserName = email,
Name = "Test Mc. Gee",
UserGroupKeys = new HashSet<Guid> { userGroup.Key }
};
var createExisting = await userService.CreateAsync(Constants.Security.SuperUserKey, createModel, true);
Assert.IsTrue(createExisting.Success);
var (updateModel, _) = await CreateUserForUpdate(userService);
updateModel.Email = email;
updateModel.UserName = email;
var updateAttempt = await userService.UpdateAsync(Constants.Security.SuperUserKey, updateModel);
Assert.IsFalse(updateAttempt.Success);
Assert.AreEqual(UserOperationStatus.DuplicateEmail, updateAttempt.Status);
}
[Test]
[TestCase("TestUser", "test@user.com", "TestUser", "another@email.com")]
[TestCase("test@email.com", "test@email.com", "test@email.com", "different@email.com")]
[TestCase("SomeName", "test@email.com", "test@email.com", "different@email.com")]
public async Task Cannot_Change_User_Name_To_Duplicate_UserName(string existingUserName, string existingEmail, string updateUserName, string updateEmail)
{
// We also ensure that your username cannot be the same as another users email.
var userService = CreateUserService(new SecuritySettings { UsernameIsEmail = false });
var userGroup = await UserGroupService.GetAsync(Constants.Security.AdminGroupAlias);
var createModel = new UserCreateModel
{
Email = existingEmail,
UserName = existingUserName,
Name = "Test Mc. Gee",
UserGroupKeys = new HashSet<Guid> { userGroup.Key }
};
var createExisting = await userService.CreateAsync(Constants.Security.SuperUserKey, createModel, true);
Assert.IsTrue(createExisting.Success);
var (updateModel, _) = await CreateUserForUpdate(userService);
updateModel.Email = updateEmail;
updateModel.UserName = updateUserName;
var updateAttempt = await userService.UpdateAsync(Constants.Security.SuperUserKey, updateModel);
Assert.IsFalse(updateAttempt.Success);
Assert.AreEqual(UserOperationStatus.DuplicateUserName, updateAttempt.Status);
}
[Test]
[TestCase("en-US", true)]
[TestCase("Very much not an ISO Code (:", false)]
[TestCase("da-ZA", false)]
public async Task Iso_Code_Is_Validated(string isoCode, bool shouldSucceed)
{
var userService = CreateUserService();
var (updateModel, _) = await CreateUserForUpdate(userService);
updateModel.LanguageIsoCode = isoCode;
var result = await userService.UpdateAsync(Constants.Security.SuperUserKey, updateModel);
if (shouldSucceed is false)
{
Assert.IsFalse(result.Success);
Assert.AreEqual(UserOperationStatus.InvalidIsoCode, result.Status);
return;
}
Assert.IsTrue(result.Success);
// We'll get the user again to ensure that the changes has been persisted
var updatedUser = await userService.GetAsync(result.Result.Key);
Assert.IsNotNull(updatedUser);
Assert.AreEqual(isoCode, updatedUser.Language);
}
// todo Ideally we would test content and media separately and together (Introduce Testcases for switching permutations)
[Test]
public async Task Can_Assign_User_Start_Nodes()
{
var contentService = GetRequiredService<IContentService>();
var mediaService = GetRequiredService<IMediaService>();
var contentStartNode = contentService.GetRootContent().First();
var mediaStartNode = mediaService.CreateMediaWithIdentity("test", -1, "Image");
var userService = CreateUserService(securitySettings: new SecuritySettings { UsernameIsEmail = false });
var (updateModel, createdUser) = await CreateUserForUpdate(userService);
updateModel.ContentStartNodeKeys = new HashSet<Guid> { contentStartNode.Key };
updateModel.MediaStartNodeKeys = new HashSet<Guid> { mediaStartNode.Key };
var result = await userService.UpdateAsync(Constants.Security.SuperUserKey, updateModel);
Assert.IsTrue(result.Success);
var updatedUser = await userService.GetAsync(createdUser.Key);
Assert.IsNotNull(updatedUser);
Assert.IsNotNull(updatedUser.StartContentIds);
Assert.AreEqual(1, updatedUser.StartContentIds.Length);
Assert.AreEqual(contentStartNode.Id, updatedUser.StartContentIds.First());
Assert.IsNotNull(updatedUser.StartMediaIds);
Assert.AreEqual(1, updatedUser.StartMediaIds.Length);
Assert.AreEqual(mediaStartNode.Id, updatedUser.StartMediaIds.First());
}
[TestCase(false, false)]
[TestCase(false, true)]
[TestCase(true, false)]
[TestCase(true, true)]
public async Task Can_Assign_Root_As_User_Start_Node(bool contentRootAccess, bool mediaRootAccess)
{
var userService = CreateUserService(securitySettings: new SecuritySettings { UsernameIsEmail = false });
var (updateModel, createdUser) = await CreateUserForUpdate(userService);
updateModel.HasContentRootAccess = contentRootAccess;
updateModel.HasMediaRootAccess = mediaRootAccess;
var result = await userService.UpdateAsync(Constants.Security.SuperUserKey, updateModel);
Assert.IsTrue(result.Success);
var updatedUser = await userService.GetAsync(createdUser.Key);
Assert.IsNotNull(updatedUser);
Assert.IsNotNull(updatedUser.StartContentIds);
if (contentRootAccess)
{
Assert.AreEqual(1, updatedUser.StartContentIds.Length);
Assert.AreEqual(Constants.System.Root, updatedUser.StartContentIds.First());
}
else
{
Assert.IsEmpty(updatedUser.StartContentIds);
}
Assert.IsNotNull(updatedUser.StartMediaIds);
if (mediaRootAccess)
{
Assert.AreEqual(1, updatedUser.StartMediaIds.Length);
Assert.AreEqual(Constants.System.Root, updatedUser.StartMediaIds.First());
}
else
{
Assert.IsEmpty(updatedUser.StartMediaIds);
}
}
// todo Ideally we would test content and media separately and together (Introduce Testcases for switching permutations)
[Test]
public async Task Can_Unassign_User_Start_Nodes()
{
var contentService = GetRequiredService<IContentService>();
var mediaService = GetRequiredService<IMediaService>();
var contentStartNode = contentService.GetRootContent().First();
var mediaStartNode = mediaService.CreateMediaWithIdentity("test", -1, "Image");
var userService = CreateUserService(securitySettings: new SecuritySettings { UsernameIsEmail = false });
var (updateModel, createdUser) = await CreateUserForUpdate(userService);
updateModel.ContentStartNodeKeys = new HashSet<Guid> { contentStartNode.Key };
updateModel.MediaStartNodeKeys = new HashSet<Guid> { mediaStartNode.Key };
await userService.UpdateAsync(Constants.Security.SuperUserKey, updateModel);
var updatedUser = await userService.GetAsync(createdUser.Key);
Assert.IsNotNull(updatedUser);
Assert.IsNotEmpty(updatedUser.StartContentIds!);
Assert.IsNotEmpty(updatedUser.StartMediaIds!);
updateModel = await MapUserToUpdateModel(updatedUser);
updateModel.ContentStartNodeKeys = new HashSet<Guid>();
updateModel.MediaStartNodeKeys = new HashSet<Guid>();
var result = await userService.UpdateAsync(Constants.Security.SuperUserKey, updateModel);
Assert.IsTrue(result.Success);
updatedUser = await userService.GetAsync(createdUser.Key);
Assert.IsNotNull(updatedUser);
Assert.IsNotNull(updatedUser.StartContentIds);
Assert.IsEmpty(updatedUser.StartContentIds);
Assert.IsNotNull(updatedUser.StartMediaIds);
Assert.IsEmpty(updatedUser.StartMediaIds);
}
// todo Ideally we would test content and media separately and together (Introduce Testcases for switching permutations)
[Test]
public async Task Can_Unassign_Root_As_User_Start_Node()
{
var userService = CreateUserService(securitySettings: new SecuritySettings { UsernameIsEmail = false });
var (updateModel, createdUser) = await CreateUserForUpdate(userService);
updateModel.HasContentRootAccess = true;
updateModel.HasMediaRootAccess = true;
await userService.UpdateAsync(Constants.Security.SuperUserKey, updateModel);
var updatedUser = await userService.GetAsync(createdUser.Key);
Assert.IsNotNull(updatedUser);
Assert.IsNotEmpty(updatedUser.StartContentIds!);
Assert.IsNotEmpty(updatedUser.StartMediaIds!);
updateModel = await MapUserToUpdateModel(updatedUser);
updateModel.HasContentRootAccess = false;
updateModel.HasMediaRootAccess = false;
var result = await userService.UpdateAsync(Constants.Security.SuperUserKey, updateModel);
Assert.IsTrue(result.Success);
updatedUser = await userService.GetAsync(createdUser.Key);
Assert.IsNotNull(updatedUser);
Assert.IsNotNull(updatedUser.StartContentIds);
Assert.IsEmpty(updatedUser.StartContentIds);
Assert.IsNotNull(updatedUser.StartMediaIds);
Assert.IsEmpty(updatedUser.StartMediaIds);
}
}