* 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.
388 lines
15 KiB
C#
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);
|
|
}
|
|
}
|