diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs index f65e7f597e..e65eafb457 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs @@ -253,7 +253,6 @@ public class BackOfficeController : SecurityControllerBase private async Task SignInBackOfficeUser(BackOfficeIdentityUser backOfficeUser, OpenIddictRequest request) { ClaimsPrincipal backOfficePrincipal = await _backOfficeSignInManager.CreateUserPrincipalAsync(backOfficeUser); - backOfficePrincipal.SetClaim(OpenIddictConstants.Claims.Subject, backOfficeUser.Key.ToString()); Claim[] backOfficeClaims = backOfficePrincipal.Claims.ToArray(); foreach (Claim backOfficeClaim in backOfficeClaims) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/SetTelemetryController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/SetTelemetryController.cs index 35dc0bba08..b0f8110e40 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/SetTelemetryController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Telemetry/SetTelemetryController.cs @@ -33,7 +33,7 @@ public class SetTelemetryController : TelemetryControllerBase return BadRequest(invalidModelProblem); } - _metricsConsentService.SetConsentLevel(telemetryRepresentationBase.TelemetryLevel); + await _metricsConsentService.SetConsentLevelAsync(telemetryRepresentationBase.TelemetryLevel); return await Task.FromResult(Ok()); } } diff --git a/src/Umbraco.Cms.Api.Management/Factories/AuditLogPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/AuditLogPresentationFactory.cs index 64ada9d074..6bde8ac27a 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/AuditLogPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/AuditLogPresentationFactory.cs @@ -17,14 +17,16 @@ public class AuditLogPresentationFactory : IAuditLogPresentationFactory private readonly MediaFileManager _mediaFileManager; private readonly IImageUrlGenerator _imageUrlGenerator; private readonly IEntityService _entityService; + private readonly IUserIdKeyResolver _userIdKeyResolver; - public AuditLogPresentationFactory(IUserService userService, AppCaches appCaches, MediaFileManager mediaFileManager, IImageUrlGenerator imageUrlGenerator, IEntityService entityService) + public AuditLogPresentationFactory(IUserService userService, AppCaches appCaches, MediaFileManager mediaFileManager, IImageUrlGenerator imageUrlGenerator, IEntityService entityService, IUserIdKeyResolver userIdKeyResolver) { _userService = userService; _appCaches = appCaches; _mediaFileManager = mediaFileManager; _imageUrlGenerator = imageUrlGenerator; _entityService = entityService; + _userIdKeyResolver = userIdKeyResolver; } public IEnumerable CreateAuditLogViewModel(IEnumerable auditItems) => auditItems.Select(CreateAuditLogViewModel); @@ -46,7 +48,8 @@ public class AuditLogPresentationFactory : IAuditLogPresentationFactory private T CreateResponseModel(IAuditItem auditItem, out IUser user) where T : AuditLogBaseModel, new() { - user = _userService.GetUserById(auditItem.UserId) + Guid userKey = _userIdKeyResolver.GetAsync(auditItem.UserId).GetAwaiter().GetResult(); + user = _userService.GetAsync(userKey).GetAwaiter().GetResult() ?? throw new ArgumentException($"Could not find user with id {auditItem.UserId}"); IEntitySlim? entitySlim = _entityService.Get(auditItem.Id); diff --git a/src/Umbraco.Cms.Api.Management/Install/CreateUnattendedUserNotificationHandler.cs b/src/Umbraco.Cms.Api.Management/Install/CreateUnattendedUserNotificationHandler.cs index a1ee165da7..0a96dcbe6b 100644 --- a/src/Umbraco.Cms.Api.Management/Install/CreateUnattendedUserNotificationHandler.cs +++ b/src/Umbraco.Cms.Api.Management/Install/CreateUnattendedUserNotificationHandler.cs @@ -52,7 +52,7 @@ public class CreateUnattendedUserNotificationHandler : INotificationAsyncHandler return; } - IUser? admin = _userService.GetUserById(Constants.Security.SuperUserId); + IUser? admin = await _userService.GetAsync(Constants.Security.SuperUserKey); if (admin == null) { throw new InvalidOperationException("Could not find the super user!"); diff --git a/src/Umbraco.Core/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Core/Cache/DistributedCacheExtensions.cs index 9a18723f59..d03d914f5e 100644 --- a/src/Umbraco.Core/Cache/DistributedCacheExtensions.cs +++ b/src/Umbraco.Core/Cache/DistributedCacheExtensions.cs @@ -32,7 +32,13 @@ public static class DistributedCacheExtensions => dc.Refresh(UserCacheRefresher.UniqueId, userId); public static void RefreshUserCache(this DistributedCache dc, IEnumerable users) - => dc.Refresh(UserCacheRefresher.UniqueId, users.Select(x => x.Id).Distinct().ToArray()); + { + foreach (IUser user in users) + { + dc.Refresh(UserCacheRefresher.UniqueId, user.Key); + dc.Refresh(UserCacheRefresher.UniqueId, user.Id); + } + } public static void RefreshAllUserCache(this DistributedCache dc) => dc.RefreshAll(UserCacheRefresher.UniqueId); diff --git a/src/Umbraco.Core/Cache/Refreshers/Implement/UserCacheRefresher.cs b/src/Umbraco.Core/Cache/Refreshers/Implement/UserCacheRefresher.cs index d1dc194f9b..094bb11a4a 100644 --- a/src/Umbraco.Core/Cache/Refreshers/Implement/UserCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/Refreshers/Implement/UserCacheRefresher.cs @@ -30,25 +30,19 @@ public sealed class UserCacheRefresher : CacheRefresherBase userCache = AppCaches.IsolatedCaches.Get(); if (userCache.Success) { - userCache.Result?.Clear(RepositoryCacheKeys.GetKey(id)); + userCache.Result?.Clear(RepositoryCacheKeys.GetKey(id)); userCache.Result?.ClearByKey(CacheKeys.UserContentStartNodePathsPrefix + id); userCache.Result?.ClearByKey(CacheKeys.UserMediaStartNodePathsPrefix + id); userCache.Result?.ClearByKey(CacheKeys.UserAllContentStartNodesPrefix + id); userCache.Result?.ClearByKey(CacheKeys.UserAllMediaStartNodesPrefix + id); } - base.Remove(id); + base.Refresh(id); } #endregion diff --git a/src/Umbraco.Core/Constants-Security.cs b/src/Umbraco.Core/Constants-Security.cs index ca8d5efb25..842add06fa 100644 --- a/src/Umbraco.Core/Constants-Security.cs +++ b/src/Umbraco.Core/Constants-Security.cs @@ -112,6 +112,11 @@ public static partial class Constants /// public const string SecurityStampClaimType = "AspNet.Identity.SecurityStamp"; + /// + /// The claim type for the mandatory OpenIdDict sub claim + /// + public const string OpenIdDictSubClaimType = "sub"; + public const string AspNetCoreV3PasswordHashAlgorithmName = "PBKDF2.ASPNETCORE.V3"; public const string AspNetCoreV2PasswordHashAlgorithmName = "PBKDF2.ASPNETCORE.V2"; public const string AspNetUmbraco8PasswordHashAlgorithmName = "HMACSHA256"; diff --git a/src/Umbraco.Core/Events/UserNotificationsHandler.cs b/src/Umbraco.Core/Events/UserNotificationsHandler.cs index 6d15ec36aa..ebc7840fa1 100644 --- a/src/Umbraco.Core/Events/UserNotificationsHandler.cs +++ b/src/Umbraco.Core/Events/UserNotificationsHandler.cs @@ -197,7 +197,7 @@ public sealed class UserNotificationsHandler : _logger.LogDebug( "There is no current Umbraco user logged in, the notifications will be sent from the administrator"); } - user = _userService.GetUserById(Constants.Security.SuperUserId); + user = _userService.GetAsync(Constants.Security.SuperUserKey).GetAwaiter().GetResult(); if (user == null) { _logger.LogWarning( diff --git a/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs b/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs index 7268012790..4ff3782019 100644 --- a/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs +++ b/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs @@ -193,6 +193,7 @@ public static class ClaimsIdentityExtensions /// /// this /// The users Id + /// The users key /// Username /// Real name /// Start content nodes @@ -201,7 +202,7 @@ public static class ClaimsIdentityExtensions /// Security stamp /// Allowed apps /// Roles - public static void AddRequiredClaims(this ClaimsIdentity identity, string userId, string username, string realName, IEnumerable? startContentNodes, IEnumerable? startMediaNodes, string culture, string securityStamp, IEnumerable allowedApps, IEnumerable roles) + public static void AddRequiredClaims(this ClaimsIdentity identity, string userId, Guid userKey, string username, string realName, IEnumerable? startContentNodes, IEnumerable? startMediaNodes, string culture, string securityStamp, IEnumerable allowedApps, IEnumerable roles) { // This is the id that 'identity' uses to check for the user id if (identity.HasClaim(x => x.Type == ClaimTypes.NameIdentifier) == false) @@ -215,6 +216,18 @@ public static class ClaimsIdentityExtensions identity)); } + // This is the id that 'identity' uses to check for the user id + if (identity.HasClaim(x => x.Type == Constants.Security.OpenIdDictSubClaimType) == false) + { + identity.AddClaim(new Claim( + Constants.Security.OpenIdDictSubClaimType, + userKey.ToString(), + ClaimValueTypes.String, + AuthenticationType, + AuthenticationType, + identity)); + } + if (identity.HasClaim(x => x.Type == ClaimTypes.Name) == false) { identity.AddClaim(new Claim( diff --git a/src/Umbraco.Core/Handlers/AuditNotificationsHandler.cs b/src/Umbraco.Core/Handlers/AuditNotificationsHandler.cs index 23393b77f0..89b15d7f37 100644 --- a/src/Umbraco.Core/Handlers/AuditNotificationsHandler.cs +++ b/src/Umbraco.Core/Handlers/AuditNotificationsHandler.cs @@ -82,7 +82,7 @@ public sealed class AuditNotificationsHandler : get { IUser? identity = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; - IUser? user = identity == null ? null : _userService.GetUserById(Convert.ToInt32(identity.Id)); + IUser? user = identity == null ? null : _userService.GetAsync(identity.Key).GetAwaiter().GetResult(); return user ?? UnknownUser(_globalSettings); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/IUserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IUserRepository.cs index dbb96478d6..0f0bdc9626 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IUserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IUserRepository.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Persistence.Querying; namespace Umbraco.Cms.Core.Persistence.Repositories; -public interface IUserRepository : IReadWriteQueryRepository +public interface IUserRepository : IReadWriteQueryRepository { /// /// Gets the count of items based on a complex query @@ -141,6 +141,4 @@ public interface IUserRepository : IReadWriteQueryRepository int ClearLoginSessions(TimeSpan timespan); void ClearLoginSession(Guid sessionId); - - IEnumerable GetNextUsers(int id, int count); } diff --git a/src/Umbraco.Core/Security/IBackofficeSecurity.cs b/src/Umbraco.Core/Security/IBackofficeSecurity.cs index 2de9104a95..d0a60e18d8 100644 --- a/src/Umbraco.Core/Security/IBackofficeSecurity.cs +++ b/src/Umbraco.Core/Security/IBackofficeSecurity.cs @@ -21,6 +21,7 @@ public interface IBackOfficeSecurity /// The current user's Id that has been authenticated for the request. /// If authentication hasn't taken place this will be unsuccessful. // TODO: This should just be an extension method on ClaimsIdentity + [Obsolete("Scheduled for removal in V15")] Attempt GetUserId(); /// diff --git a/src/Umbraco.Core/Services/IMetricsConsentService.cs b/src/Umbraco.Core/Services/IMetricsConsentService.cs index 72f3ebe873..e210a8925d 100644 --- a/src/Umbraco.Core/Services/IMetricsConsentService.cs +++ b/src/Umbraco.Core/Services/IMetricsConsentService.cs @@ -6,5 +6,8 @@ public interface IMetricsConsentService { TelemetryLevel GetConsentLevel(); + [Obsolete("Please use SetConsentLevelAsync instead, scheduled for removal in V15")] void SetConsentLevel(TelemetryLevel telemetryLevel); + + Task SetConsentLevelAsync(TelemetryLevel telemetryLevel); } diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs index 8d6259b171..d386800f21 100644 --- a/src/Umbraco.Core/Services/IUserService.cs +++ b/src/Umbraco.Core/Services/IUserService.cs @@ -322,8 +322,6 @@ public interface IUserService : IMembershipUserService /// IEnumerable GetAllNotInGroup(int groupId); - IEnumerable GetNextUsers(int id, int count); - #region User groups /// diff --git a/src/Umbraco.Core/Services/MemberService.cs b/src/Umbraco.Core/Services/MemberService.cs index 2a83960cf2..6435bf5c5c 100644 --- a/src/Umbraco.Core/Services/MemberService.cs +++ b/src/Umbraco.Core/Services/MemberService.cs @@ -20,8 +20,8 @@ namespace Umbraco.Cms.Core.Services private readonly IMemberTypeRepository _memberTypeRepository; private readonly IMemberGroupRepository _memberGroupRepository; private readonly IAuditRepository _auditRepository; - private readonly IMemberGroupService _memberGroupService; + private readonly Lazy _idKeyMap; #region Constructor @@ -33,13 +33,15 @@ namespace Umbraco.Cms.Core.Services IMemberRepository memberRepository, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, - IAuditRepository auditRepository) + IAuditRepository auditRepository, + Lazy idKeyMap) : base(provider, loggerFactory, eventMessagesFactory) { _memberRepository = memberRepository; _memberTypeRepository = memberTypeRepository; _memberGroupRepository = memberGroupRepository; _auditRepository = auditRepository; + _idKeyMap = idKeyMap; _memberGroupService = memberGroupService ?? throw new ArgumentNullException(nameof(memberGroupService)); } @@ -333,8 +335,7 @@ namespace Umbraco.Cms.Core.Services { using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true); scope.ReadLock(Constants.Locks.MemberTree); - IQuery query = Query().Where(x => x.Key == id); - return _memberRepository.Get(query)?.FirstOrDefault(); + return GetMemberFromRepository(id); } [Obsolete($"Use {nameof(GetById)}. Will be removed in V15.")] @@ -1069,6 +1070,12 @@ namespace Umbraco.Cms.Core.Services private void Audit(AuditType type, int userId, int objectId, string? message = null) => _auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.Member), message)); + private IMember? GetMemberFromRepository(Guid id) + => _idKeyMap.Value.GetIdForKey(id, UmbracoObjectTypes.Member) switch + { + { Success: false } => null, + { Result: var intId } => _memberRepository.Get(intId), + }; #endregion #region Membership diff --git a/src/Umbraco.Core/Services/MetricsConsentService.cs b/src/Umbraco.Core/Services/MetricsConsentService.cs index 49636d77b2..3a560e76d2 100644 --- a/src/Umbraco.Core/Services/MetricsConsentService.cs +++ b/src/Umbraco.Core/Services/MetricsConsentService.cs @@ -39,13 +39,12 @@ public class MetricsConsentService : IMetricsConsentService return analyticsLevel; } - public void SetConsentLevel(TelemetryLevel telemetryLevel) + [Obsolete("Please use SetConsentLevelAsync instead, scheduled for removal in V15")] + public void SetConsentLevel(TelemetryLevel telemetryLevel) => SetConsentLevelAsync(telemetryLevel).GetAwaiter().GetResult(); + + public async Task SetConsentLevelAsync(TelemetryLevel telemetryLevel) { - IUser? currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; - if (currentUser is null) - { - currentUser = _userService.GetUserById(Constants.Security.SuperUserId); - } + IUser? currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser ?? await _userService.GetAsync(Constants.Security.SuperUserKey); _logger.LogInformation("Telemetry level set to {telemetryLevel} by {username}", telemetryLevel, currentUser?.Username); _keyValueService.SetValue(Key, telemetryLevel.ToString()); diff --git a/src/Umbraco.Core/Services/NotificationService.cs b/src/Umbraco.Core/Services/NotificationService.cs index 336435d97a..142738d49f 100644 --- a/src/Umbraco.Core/Services/NotificationService.cs +++ b/src/Umbraco.Core/Services/NotificationService.cs @@ -94,52 +94,41 @@ public class NotificationService : INotificationService // lazily get versions var prevVersionDictionary = new Dictionary(); - // see notes above - var id = Constants.Security.SuperUserId; - const int pagesz = 400; // load batches of 400 users - do + var notifications = GetUsersNotifications(new List(), action, Enumerable.Empty(), Constants.ObjectTypes.Document)?.ToList(); + if (notifications is null || notifications.Count == 0) { - var notifications = GetUsersNotifications(new List(), action, Enumerable.Empty(), Constants.ObjectTypes.Document)?.ToList(); - if (notifications is null || notifications.Count == 0) + return; + } + + IUser[] users = _userService.GetAll(0, int.MaxValue, out _).ToArray(); + foreach (IUser user in users) + { + Notification[] userNotifications = notifications.Where(n => n.UserId == user.Id).ToArray(); + foreach (Notification notification in userNotifications) { + // notifications are inherited down the tree - find the topmost entity + // relevant to this notification (entity list is sorted by path) + IContent? entityForNotification = entitiesL + .FirstOrDefault(entity => + pathsByEntityId.TryGetValue(entity.Id, out var path) && + path.Contains(notification.EntityId)); + + if (entityForNotification == null) + { + continue; + } + + if (prevVersionDictionary.ContainsKey(entityForNotification.Id) == false) + { + prevVersionDictionary[entityForNotification.Id] = GetPreviousVersion(entityForNotification.Id); + } + + // queue notification + NotificationRequest req = CreateNotificationRequest(operatingUser, user, entityForNotification, prevVersionDictionary[entityForNotification.Id], actionName, siteUri, createSubject, createBody); + Enqueue(req); break; } - - // users are returned ordered by id, notifications are returned ordered by user id - var users = _userService.GetNextUsers(id, pagesz).Where(x => x.IsApproved).ToList(); - foreach (IUser user in users) - { - Notification[] userNotifications = notifications.Where(n => n.UserId == user.Id).ToArray(); - foreach (Notification notification in userNotifications) - { - // notifications are inherited down the tree - find the topmost entity - // relevant to this notification (entity list is sorted by path) - IContent? entityForNotification = entitiesL - .FirstOrDefault(entity => - pathsByEntityId.TryGetValue(entity.Id, out var path) && - path.Contains(notification.EntityId)); - - if (entityForNotification == null) - { - continue; - } - - if (prevVersionDictionary.ContainsKey(entityForNotification.Id) == false) - { - prevVersionDictionary[entityForNotification.Id] = GetPreviousVersion(entityForNotification.Id); - } - - // queue notification - NotificationRequest req = CreateNotificationRequest(operatingUser, user, entityForNotification, prevVersionDictionary[entityForNotification.Id], actionName, siteUri, createSubject, createBody); - Enqueue(req); - break; - } - } - - // load more users if any - id = users.Count == pagesz ? users.Last().Id + 1 : -1; } - while (id > 0); } /// diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 00d158590a..89791e0ad6 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Editors; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Exceptions; @@ -50,7 +51,9 @@ internal class UserService : RepositoryService, IUserService private readonly IIsoCodeValidator _isoCodeValidator; private readonly IUserRepository _userRepository; private readonly ContentSettings _contentSettings; + private readonly IUserIdKeyResolver _userIdKeyResolver; + [Obsolete("Use the constructor that takes an IUserIdKeyResolver instead. Scheduled for removal in V15.")] public UserService( ICoreScopeProvider provider, ILoggerFactory loggerFactory, @@ -70,6 +73,49 @@ internal class UserService : RepositoryService, IUserService IOptions contentSettings, IIsoCodeValidator isoCodeValidator, IUserForgotPasswordSender forgotPasswordSender) + : this( + provider, + loggerFactory, + eventMessagesFactory, + userRepository, + userGroupRepository, + globalSettings, + securitySettings, + userEditorAuthorizationHelper, + serviceScopeFactory, + entityService, + localLoginSettingProvider, + inviteSender, + mediaFileManager, + temporaryFileService, + shortStringHelper, + contentSettings, + isoCodeValidator, + forgotPasswordSender, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + public UserService( + ICoreScopeProvider provider, + ILoggerFactory loggerFactory, + IEventMessagesFactory eventMessagesFactory, + IUserRepository userRepository, + IUserGroupRepository userGroupRepository, + IOptions globalSettings, + IOptions securitySettings, + UserEditorAuthorizationHelper userEditorAuthorizationHelper, + IServiceScopeFactory serviceScopeFactory, + IEntityService entityService, + ILocalLoginSettingProvider localLoginSettingProvider, + IUserInviteSender inviteSender, + MediaFileManager mediaFileManager, + ITemporaryFileService temporaryFileService, + IShortStringHelper shortStringHelper, + IOptions contentSettings, + IIsoCodeValidator isoCodeValidator, + IUserForgotPasswordSender forgotPasswordSender, + IUserIdKeyResolver userIdKeyResolver) : base(provider, loggerFactory, eventMessagesFactory) { _userRepository = userRepository; @@ -84,6 +130,7 @@ internal class UserService : RepositoryService, IUserService _shortStringHelper = shortStringHelper; _isoCodeValidator = isoCodeValidator; _forgotPasswordSender = forgotPasswordSender; + _userIdKeyResolver = userIdKeyResolver; _globalSettings = globalSettings.Value; _securitySettings = securitySettings.Value; _contentSettings = contentSettings.Value; @@ -187,11 +234,13 @@ internal class UserService : RepositoryService, IUserService /// /// /// + [Obsolete("Please use GetAsync instead. Scheduled for removal in V15.")] public IUser? GetById(int id) { using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true)) { - return _userRepository.Get(id); + Guid userKey = _userIdKeyResolver.GetAsync(id).GetAwaiter().GetResult(); + return _userRepository.Get(userKey); } } @@ -1669,14 +1718,6 @@ internal class UserService : RepositoryService, IUserService } } - public IEnumerable GetNextUsers(int id, int count) - { - using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true)) - { - return _userRepository.GetNextUsers(id, count); - } - } - /// /// Gets a list of objects associated with a given group /// @@ -1969,10 +2010,17 @@ internal class UserService : RepositoryService, IUserService userGroup.HasIdentity ? _userRepository.GetAllInGroup(userGroup.Id).ToArray() : empty; var xGroupUsers = groupUsers.ToDictionary(x => x.Id, x => x); var groupIds = groupUsers.Select(x => x.Id).ToArray(); + var addedUserKeys = new List(); + foreach (var userId in userIds.Except(groupIds)) + { + Guid userKey = _userIdKeyResolver.GetAsync(userId).GetAwaiter().GetResult(); + addedUserKeys.Add(userKey); + } + IEnumerable addedUserIds = userIds.Except(groupIds); addedUsers = addedUserIds.Count() > 0 - ? _userRepository.GetMany(addedUserIds.ToArray()).Where(x => x.Id != 0).ToArray() + ? _userRepository.GetMany(addedUserKeys.ToArray()).Where(x => x.Id != 0).ToArray() : new IUser[] { }; removedUsers = groupIds.Except(userIds).Select(x => xGroupUsers[x]).Where(x => x.Id != 0).ToArray(); } diff --git a/src/Umbraco.Infrastructure/Installer/Steps/CreateUserStep.cs b/src/Umbraco.Infrastructure/Installer/Steps/CreateUserStep.cs index 09a291d527..b4fe7b23b9 100644 --- a/src/Umbraco.Infrastructure/Installer/Steps/CreateUserStep.cs +++ b/src/Umbraco.Infrastructure/Installer/Steps/CreateUserStep.cs @@ -59,7 +59,7 @@ public class CreateUserStep : StepBase, IInstallStep public async Task> ExecuteAsync(InstallData model) { - IUser? admin = _userService.GetUserById(Constants.Security.SuperUserId); + IUser? admin = _userService.GetAsync(Constants.Security.SuperUserKey).GetAwaiter().GetResult(); if (admin is null) { return FailWithMessage("Could not find the super user"); @@ -92,7 +92,7 @@ public class CreateUserStep : StepBase, IInstallStep return FailWithMessage("Could not reset password: " + string.Join(", ", resetResult.Errors.ToErrorMessage())); } - _metricsConsentService.SetConsentLevel(model.TelemetryLevel); + await _metricsConsentService.SetConsentLevelAsync(model.TelemetryLevel); if (model.User.SubscribeToNewsletter) { diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs index cb86f717fe..44750f3293 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs @@ -25,7 +25,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; /// /// Represents the UserRepository for doing CRUD operations for /// -internal class UserRepository : EntityRepositoryBase, IUserRepository +internal class UserRepository : EntityRepositoryBase, IUserRepository { private readonly IMapperCollection _mapperCollection; private readonly GlobalSettings _globalSettings; @@ -106,34 +106,14 @@ internal class UserRepository : EntityRepositoryBase, IUserRepositor private IEnumerable ConvertFromDtos(IEnumerable dtos) => dtos.Select(x => UserFactory.BuildEntity(_globalSettings, x, _permissionMappers)); - #region Overrides of RepositoryBase + #region Overrides of RepositoryBase - protected override IUser? PerformGet(int id) + protected override IUser? PerformGet(Guid key) { - // This will never resolve to a user, yet this is asked - // for all of the time (especially in cases of members). - // Don't issue a SQL call for this, we know it will not exist. - if (_runtimeState.Level == RuntimeLevel.Upgrade) - { - // when upgrading people might come from version 7 where user 0 was the default, - // only in upgrade mode do we want to fetch the user of Id 0 - if (id < -1) - { - return null; - } - } - else - { - if (id == default || id < -1) - { - return null; - } - } - Sql sql = SqlContext.Sql() .Select() .From() - .Where(x => x.Id == id); + .Where(x => x.Key == key); List? dtos = Database.Fetch(sql); if (dtos.Count == 0) @@ -145,6 +125,8 @@ internal class UserRepository : EntityRepositoryBase, IUserRepositor return UserFactory.BuildEntity(_globalSettings, dtos[0], _permissionMappers); } + protected override Guid GetEntityId(IUser entity) => entity.Key; + /// /// Returns a user by username /// @@ -345,11 +327,12 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 .Update(u => u.Set(x => x.LoggedOutUtc, DateTime.UtcNow)) .Where(x => x.SessionId == sessionId)); - protected override IEnumerable PerformGetAll(params int[]? ids) + + protected override IEnumerable PerformGetAll(params Guid[]? ids) { List dtos = ids?.Length == 0 ? GetDtosWith(null, true) - : GetDtosWith(sql => sql.WhereIn(x => x.Id, ids), true); + : GetDtosWith(sql => sql.WhereIn(x => x.Key, ids), true); var users = new IUser[dtos.Count]; var i = 0; foreach (UserDto dto in dtos) @@ -683,12 +666,13 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 }; return list; } + protected override void PersistDeletedItem(IUser entity) { IEnumerable deletes = GetDeleteClauses(); foreach (var delete in deletes) { - Database.Execute(delete, new { id = GetEntityId(entity), key = entity.Key }); + Database.Execute(delete, new { id = entity.Id, key = GetEntityId(entity) }); } entity.DeleteDate = DateTime.Now; @@ -911,6 +895,16 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 return Database.ExecuteScalar(sql); } + protected override bool PerformExists(Guid key) + { + Sql sql = SqlContext.Sql() + .SelectCount() + .From() + .Where(x => x.Key == key); + + return Database.ExecuteScalar(sql) > 0; + } + public bool Exists(string username) => ExistsByUserName(username); public bool ExistsByUserName(string username) @@ -1194,23 +1188,5 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 return sql; } - - public IEnumerable GetNextUsers(int id, int count) - { - Sql idsQuery = SqlContext.Sql() - .Select(x => x.Id) - .From() - .Where(x => x.Id >= id) - .OrderBy(x => x.Id); - - // first page is index 1, not zero - var ids = Database.Page(1, count, idsQuery).Items.ToArray(); - - // now get the actual users and ensure they are ordered properly (same clause) - return ids.Length == 0 - ? Enumerable.Empty() - : GetMany(ids).OrderBy(x => x.Id) ?? Enumerable.Empty(); - } - #endregion } diff --git a/src/Umbraco.Infrastructure/Security/BackOfficeClaimsPrincipalFactory.cs b/src/Umbraco.Infrastructure/Security/BackOfficeClaimsPrincipalFactory.cs index c1a69d24e7..eeac48801d 100644 --- a/src/Umbraco.Infrastructure/Security/BackOfficeClaimsPrincipalFactory.cs +++ b/src/Umbraco.Infrastructure/Security/BackOfficeClaimsPrincipalFactory.cs @@ -51,6 +51,7 @@ public class BackOfficeClaimsPrincipalFactory : UserClaimsPrincipalFactory query = _scopeProvider.CreateQuery().Where(x => x.Id == id); + return Task.FromResult(_userRepository.Get(query).FirstOrDefault()); } catch (DbException) { @@ -269,13 +270,14 @@ public class BackOfficeUserStore : public Task> GetUsersAsync(params int[]? ids) { - if (ids?.Length <= 0) + if (ids is null || ids.Length <= 0) { return Task.FromResult(Enumerable.Empty()); } using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true); - IEnumerable users = _userRepository.GetMany(ids); + IQuery query = _scopeProvider.CreateQuery().Where(x => ids.Contains(x.Id)); + IEnumerable users = _userRepository.Get(query); return Task.FromResult(users); } @@ -288,8 +290,7 @@ public class BackOfficeUserStore : } using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true); - IQuery query = _scopeProvider.CreateQuery().Where(x => keys.Contains(x.Key)); - IEnumerable users = _userRepository.Get(query); + IEnumerable users = _userRepository.GetMany(keys); return Task.FromResult(users); } @@ -298,8 +299,7 @@ public class BackOfficeUserStore : public Task GetAsync(Guid key) { using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true); - IQuery query = _scopeProvider.CreateQuery().Where(x => x.Key == key); - return Task.FromResult(_userRepository.Get(query).FirstOrDefault()); + return Task.FromResult(_userRepository.Get(key)); } /// diff --git a/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs b/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs index f772ad583c..270bea8f01 100644 --- a/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs +++ b/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs @@ -36,11 +36,8 @@ public class BackOfficeSecurity : IBackOfficeSecurity // Check again if (_currentUser == null) { - Attempt id = GetUserId(); - if (id.Success) - { - _currentUser = id.Success ? _userService.GetUserById(id.Result) : null; - } + Attempt keyAttempt = GetUserKey(); + _currentUser = keyAttempt.Success ? _userService.GetAsync(keyAttempt.Result).GetAwaiter().GetResult() : null; } } } @@ -49,6 +46,14 @@ public class BackOfficeSecurity : IBackOfficeSecurity } } + private Attempt GetUserKey() + { + ClaimsIdentity? identity = _httpContextAccessor.HttpContext?.GetCurrentIdentity(); + + Guid? id = identity?.GetUserKey(); + return id.HasValue is false ? Attempt.Fail() : Attempt.Succeed(id.Value); + } + /// public Attempt GetUserId() { diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MetricsConsentServiceTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MetricsConsentServiceTest.cs index a1fdabe686..b3c55c5dea 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MetricsConsentServiceTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/MetricsConsentServiceTest.cs @@ -18,9 +18,9 @@ public class MetricsConsentServiceTest : UmbracoIntegrationTest [TestCase(TelemetryLevel.Minimal)] [TestCase(TelemetryLevel.Basic)] [TestCase(TelemetryLevel.Detailed)] - public void Can_Store_Consent(TelemetryLevel level) + public async Task Can_Store_Consent(TelemetryLevel level) { - MetricsConsentService.SetConsentLevel(level); + await MetricsConsentService.SetConsentLevelAsync(level); var actual = MetricsConsentService.GetConsentLevel(); Assert.IsNotNull(actual); @@ -28,9 +28,9 @@ public class MetricsConsentServiceTest : UmbracoIntegrationTest } [Test] - public void Enum_Stored_as_string() + public async Task Enum_Stored_as_string() { - MetricsConsentService.SetConsentLevel(TelemetryLevel.Detailed); + await MetricsConsentService.SetConsentLevelAsync(TelemetryLevel.Detailed); var stringValue = KeyValueService.GetValue(Cms.Core.Services.MetricsConsentService.Key); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/UserServiceCrudTests.Update.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/UserServiceCrudTests.Update.cs index acb035774e..12f430cf1f 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/UserServiceCrudTests.Update.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/UserServiceCrudTests.Update.cs @@ -126,6 +126,34 @@ public partial class UserServiceCrudTests 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() { diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Telemetry/TelemetryServiceTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Telemetry/TelemetryServiceTests.cs index 03c4fd8633..f736240e76 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Telemetry/TelemetryServiceTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Telemetry/TelemetryServiceTests.cs @@ -23,7 +23,7 @@ public class TelemetryServiceTests : UmbracoIntegrationTest private IMetricsConsentService MetricsConsentService => GetRequiredService(); [Test] - public void Expected_Detailed_Telemetry_Exists() + public async Task Expected_Detailed_Telemetry_Exists() { var expectedData = new[] { @@ -54,7 +54,7 @@ public class TelemetryServiceTests : UmbracoIntegrationTest Constants.Telemetry.DeliveryApiPublicAccess }; - MetricsConsentService.SetConsentLevel(TelemetryLevel.Detailed); + await MetricsConsentService.SetConsentLevelAsync(TelemetryLevel.Detailed); var success = TelemetryService.TryGetTelemetryReportData(out var telemetryReportData); var detailed = telemetryReportData.Detailed.ToArray(); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/BackOfficeExamineSearcherTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/BackOfficeExamineSearcherTests.cs index f0545a552d..e7d8ebbdb2 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/BackOfficeExamineSearcherTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/BackOfficeExamineSearcherTests.cs @@ -85,8 +85,7 @@ public class BackOfficeExamineSearcherTests : ExamineBaseTest private async Task SetupUserIdentity(string userId) { - var identity = - await BackOfficeUserStore.FindByIdAsync(userId, CancellationToken.None); + var identity = await BackOfficeUserStore.FindByIdAsync(userId, CancellationToken.None); await BackOfficeSignInManager.SignInAsync(identity, false); var principal = await BackOfficeSignInManager.CreateUserPrincipalAsync(identity); HttpContextAccessor.HttpContext.SetPrincipalForRequest(principal); @@ -595,7 +594,7 @@ public class BackOfficeExamineSearcherTests : ExamineBaseTest public async Task Check_All_Indexed_Values_For_Published_Content_With_No_Properties() { // Arrange - await SetupUserIdentity(Constants.Security.SuperUserIdAsString); + await SetupUserIdentity(Constants.Security.SuperUserKey.ToString()); const string contentName = "TestContent"; diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/UserRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/UserRepositoryTest.cs index cce400878a..a5ed0b40ef 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/UserRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/UserRepositoryTest.cs @@ -126,7 +126,7 @@ public class UserRepositoryTest : UmbracoIntegrationTest repository.Save(user); // Act - var resolved = repository.Get(user.Id); + var resolved = repository.Get(user.Key); var dirty = ((User)resolved).IsDirty(); // Assert @@ -148,7 +148,7 @@ public class UserRepositoryTest : UmbracoIntegrationTest // Act repository.Save(user); - var id = user.Id; + var id = user.Key; var mockRuntimeState = CreateMockRuntimeState(RuntimeLevel.Run); @@ -185,7 +185,7 @@ public class UserRepositoryTest : UmbracoIntegrationTest var user = CreateAndCommitUserWithGroup(repository, userGroupRepository); // Act - var updatedItem = repository.Get(user.Id); + var updatedItem = repository.Get(user.Key); // TODO: this test cannot work, user has 2 sections but the way it's created, // they don't show, so the comparison with updatedItem fails - fix! @@ -227,7 +227,7 @@ public class UserRepositoryTest : UmbracoIntegrationTest var users = CreateAndCommitMultipleUsers(repository); // Act - var result = repository.GetMany(users[0].Id, users[1].Id).ToArray(); + var result = repository.GetMany(users[0].Key, users[1].Key).ToArray(); // Assert Assert.That(result, Is.Not.Null); @@ -269,7 +269,7 @@ public class UserRepositoryTest : UmbracoIntegrationTest var users = CreateAndCommitMultipleUsers(repository); // Act - var exists = repository.Exists(users[0].Id); + var exists = repository.Exists(users[0].Key); // Assert Assert.That(exists, Is.True); @@ -396,7 +396,7 @@ public class UserRepositoryTest : UmbracoIntegrationTest repository.Save(user); // Get the user - var updatedUser = repository.Get(user.Id); + var updatedUser = repository.Get(user.Key); // Ensure the Security Stamp is invalidated & no longer the same Assert.AreNotEqual(originalSecurityStamp, updatedUser.SecurityStamp); @@ -460,7 +460,7 @@ public class UserRepositoryTest : UmbracoIntegrationTest var user = CreateAndCommitUserWithGroup(userRepository, userGroupRepository); // Act - var resolved = (User)userRepository.Get(user.Id); + var resolved = (User)userRepository.Get(user.Key); resolved.Name = "New Name"; @@ -478,7 +478,7 @@ public class UserRepositoryTest : UmbracoIntegrationTest userRepository.Save(resolved); - var updatedItem = (User)userRepository.Get(user.Id); + var updatedItem = (User)userRepository.Get(user.Key); // Assert Assert.That(updatedItem.Id, Is.EqualTo(resolved.Id)); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopedRepositoryTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopedRepositoryTests.cs index 0c09bf14e8..8e9206b1bf 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopedRepositoryTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopedRepositoryTests.cs @@ -63,7 +63,7 @@ public class ScopedRepositoryTests : UmbracoIntegrationTest [TestCase(true)] [TestCase(false)] - public void DefaultRepositoryCachePolicy(bool complete) + public async Task DefaultRepositoryCachePolicy(bool complete) { var scopeProvider = (ScopeProvider)ScopeProvider; var service = (UserService)UserService; @@ -72,13 +72,13 @@ public class ScopedRepositoryTests : UmbracoIntegrationTest service.Save(user); // User has been saved so the cache has been cleared of it - var globalCached = (IUser)globalCache.Get(GetCacheIdKey(user.Id), () => null); + var globalCached = (IUser)globalCache.Get(GetCacheIdKey(user.Key), () => null); Assert.IsNull(globalCached); // Get user again to load it into the cache again, this also ensure we don't modify the one that's in the cache. - user = service.GetUserById(user.Id); + user = await service.GetAsync(user.Key); // global cache contains the entity - globalCached = (IUser)globalCache.Get(GetCacheIdKey(user.Id), () => null); + globalCached = (IUser)globalCache.Get(GetCacheIdKey(user.Key), () => null); Assert.IsNotNull(globalCached); Assert.AreEqual(user.Id, globalCached.Id); Assert.AreEqual("name", globalCached.Name); @@ -104,7 +104,7 @@ public class ScopedRepositoryTests : UmbracoIntegrationTest Assert.AreEqual("changed", scopeCached.Name); // global cache is unchanged - globalCached = (IUser)globalCache.Get(GetCacheIdKey(user.Id), () => null); + globalCached = (IUser)globalCache.Get(GetCacheIdKey(user.Key), () => null); Assert.IsNotNull(globalCached); Assert.AreEqual(user.Id, globalCached.Id); Assert.AreEqual("name", globalCached.Name); @@ -117,7 +117,7 @@ public class ScopedRepositoryTests : UmbracoIntegrationTest Assert.IsNull(scopeProvider.AmbientScope); - globalCached = (IUser)globalCache.Get(GetCacheIdKey(user.Id), () => null); + globalCached = (IUser)globalCache.Get(GetCacheIdKey(user.Key), () => null); if (complete) { // global cache has been cleared @@ -130,11 +130,11 @@ public class ScopedRepositoryTests : UmbracoIntegrationTest } // get again, updated if completed - user = service.GetUserById(user.Id); + user = await service.GetAsync(user.Key); Assert.AreEqual(complete ? "changed" : "name", user.Name); // global cache contains the entity again - globalCached = (IUser)globalCache.Get(GetCacheIdKey(user.Id), () => null); + globalCached = (IUser)globalCache.Get(GetCacheIdKey(user.Key), () => null); Assert.IsNotNull(globalCached); Assert.AreEqual(user.Id, globalCached.Id); Assert.AreEqual(complete ? "changed" : "name", globalCached.Name); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/UmbracoBackOfficeIdentityTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/UmbracoBackOfficeIdentityTests.cs index d1e980b19c..a8ac651f9f 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/UmbracoBackOfficeIdentityTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/UmbracoBackOfficeIdentityTests.cs @@ -111,6 +111,7 @@ public class UmbracoBackOfficeIdentityTests claimsIdentity.AddRequiredClaims( "1234", + Guid.NewGuid(), "testing", "hello world", new[] { 654 }, @@ -120,7 +121,7 @@ public class UmbracoBackOfficeIdentityTests new[] { "content", "media" }, new[] { "admin" }); - Assert.AreEqual(12, claimsIdentity.Claims.Count()); + Assert.AreEqual(13, claimsIdentity.Claims.Count()); Assert.IsNull(claimsIdentity.Actor); } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/ClaimsPrincipalExtensionsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/ClaimsPrincipalExtensionsTests.cs index 9d5c4bb48a..4e21e422bd 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/ClaimsPrincipalExtensionsTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/ClaimsPrincipalExtensionsTests.cs @@ -18,6 +18,7 @@ public class ClaimsPrincipalExtensionsTests var backOfficeIdentity = new ClaimsIdentity(); backOfficeIdentity.AddRequiredClaims( Constants.Security.SuperUserIdAsString, + Constants.Security.SuperUserKey, "test", "test", Enumerable.Empty(), @@ -55,6 +56,7 @@ public class ClaimsPrincipalExtensionsTests var backOfficeIdentity = new ClaimsIdentity(); backOfficeIdentity.AddRequiredClaims( Constants.Security.SuperUserIdAsString, + Constants.Security.SuperUserKey, "test", "test", Enumerable.Empty(),