From 3f2d1fd7a8f016b0e13d10e0289b23558ec81dc2 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 20 Nov 2023 15:29:10 +0800 Subject: [PATCH] Clear Dynamic claims cache when role or organization are changed. --- .../Abp/Identity/IIdentityUserRepository.cs | 5 + .../Identity/IOrganizationUnitRepository.cs | 5 + ...yDynamicClaimsPrincipalContributorCache.cs | 14 +- .../Volo/Abp/Identity/IdentityRoleManager.cs | 30 +++- .../Volo/Abp/Identity/IdentityUserManager.cs | 47 +++++- .../Abp/Identity/OrganizationUnitManager.cs | 27 ++- .../UserEntityUpdatedOrDeletedEventHandler.cs | 47 ++++++ .../Abp/Identity/UserUpdatedEventHandler.cs | 40 ----- .../EfCoreIdentityUserRepository.cs | 6 + .../EfCoreOrganizationUnitRepository.cs | 10 ++ .../MongoDB/MongoIdentityUserRepository.cs | 28 +++- .../MongoOrganizationUnitRepository.cs | 8 + ...DynamicClaimsPrincipalContributor_Tests.cs | 155 +++++++++++++++++- .../Identity/AbpIdentityTestDataBuilder.cs | 5 +- .../Identity/IdentityRoleRepository_Tests.cs | 5 +- .../Volo/Abp/Identity/IdentityTestData.cs | 4 +- .../Identity/IdentityUserRepository_Tests.cs | 27 ++- .../OrganizationUnitRepository_Tests.cs | 11 ++ 18 files changed, 398 insertions(+), 76 deletions(-) create mode 100644 modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserEntityUpdatedOrDeletedEventHandler.cs delete mode 100644 modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserUpdatedEventHandler.cs diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs index 1a5281e44c..b8d5679aff 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs @@ -50,6 +50,11 @@ public interface IIdentityUserRepository : IBasicRepository CancellationToken cancellationToken = default ); + Task> GetUserIdListByRoleIdAsync( + Guid roleId, + CancellationToken cancellationToken = default + ); + Task> GetListAsync( string sorting = null, int maxResultCount = int.MaxValue, diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs index 511b571fee..047e01193a 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs @@ -90,6 +90,11 @@ public interface IOrganizationUnitRepository : IBasicRepository> GetMemberIdsAsync( + Guid id, + CancellationToken cancellationToken = default + ); + Task GetMembersCountAsync( OrganizationUnit organizationUnit, string filter = null, diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributorCache.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributorCache.cs index 74d395e692..6051914adc 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributorCache.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributorCache.cs @@ -18,7 +18,7 @@ public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependen { public ILogger Logger { get; set; } - protected IDistributedCache Cache { get; } + protected IDistributedCache DynamicClaimCache { get; } protected ICurrentTenant CurrentTenant { get; } protected IdentityUserManager UserManager { get; } protected IUserClaimsPrincipalFactory UserClaimsPrincipalFactory { get; } @@ -26,14 +26,14 @@ public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependen protected IOptions CacheOptions { get; } public IdentityDynamicClaimsPrincipalContributorCache( - IDistributedCache cache, + IDistributedCache dynamicClaimCache, ICurrentTenant currentTenant, IdentityUserManager userManager, IUserClaimsPrincipalFactory userClaimsPrincipalFactory, IOptions abpClaimsPrincipalFactoryOptions, IOptions cacheOptions) { - Cache = cache; + DynamicClaimCache = dynamicClaimCache; CurrentTenant = currentTenant; UserManager = userManager; UserClaimsPrincipalFactory = userClaimsPrincipalFactory; @@ -50,7 +50,7 @@ public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependen if (AbpClaimsPrincipalFactoryOptions.Value.DynamicClaims.IsNullOrEmpty()) { var emptyCacheItem = new AbpDynamicClaimCacheItem(); - await Cache.SetAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId), emptyCacheItem, new DistributedCacheEntryOptions + await DynamicClaimCache.SetAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId), emptyCacheItem, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = CacheOptions.Value.CacheAbsoluteExpiration }); @@ -58,7 +58,7 @@ public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependen return emptyCacheItem; } - return await Cache.GetOrAddAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId), async () => + return await DynamicClaimCache.GetOrAddAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId), async () => { using (CurrentTenant.Change(tenantId)) { @@ -91,7 +91,7 @@ public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependen public virtual async Task ClearAsync(Guid userId, Guid? tenantId = null) { - Logger.LogDebug($"Clearing dynamic claims cache for user: {userId}"); - await Cache.RemoveAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId)); + Logger.LogDebug($"Remove dynamic claims cache for user: {userId}"); + await DynamicClaimCache.RemoveAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId)); } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleManager.cs index e6f8919c17..3dc8dacc32 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleManager.cs @@ -6,9 +6,11 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; +using Volo.Abp.Caching; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Services; using Volo.Abp.Identity.Localization; +using Volo.Abp.Security.Claims; using Volo.Abp.Threading; namespace Volo.Abp.Identity; @@ -19,6 +21,8 @@ public class IdentityRoleManager : RoleManager, IDomainService protected IStringLocalizer Localizer { get; } protected ICancellationTokenProvider CancellationTokenProvider { get; } + protected IIdentityUserRepository UserRepository { get; } + protected IDistributedCache DynamicClaimCache { get; } public IdentityRoleManager( IdentityRoleStore store, @@ -27,7 +31,9 @@ public class IdentityRoleManager : RoleManager, IDomainService IdentityErrorDescriber errors, ILogger logger, IStringLocalizer localizer, - ICancellationTokenProvider cancellationTokenProvider) + ICancellationTokenProvider cancellationTokenProvider, + IIdentityUserRepository userRepository, + IDistributedCache dynamicClaimCache) : base( store, roleValidators, @@ -37,6 +43,8 @@ public class IdentityRoleManager : RoleManager, IDomainService { Localizer = localizer; CancellationTokenProvider = cancellationTokenProvider; + UserRepository = userRepository; + DynamicClaimCache = dynamicClaimCache; } public virtual async Task GetByIdAsync(Guid id) @@ -57,7 +65,15 @@ public class IdentityRoleManager : RoleManager, IDomainService throw new BusinessException(IdentityErrorCodes.StaticRoleRenaming); } - return await base.SetRoleNameAsync(role, name); + var userIdList = await UserRepository.GetUserIdListByRoleIdAsync(role.Id, cancellationToken: CancellationToken); + var result = await base.SetRoleNameAsync(role, name); + if (result.Succeeded) + { + Logger.LogDebug($"Remove dynamic claims cache for users of role: {role.Id}"); + await DynamicClaimCache.RemoveManyAsync(userIdList.Select(userId => AbpDynamicClaimCacheItem.CalculateCacheKey(userId, role.TenantId)), token: CancellationToken); + } + + return result; } public async override Task DeleteAsync(IdentityRole role) @@ -67,6 +83,14 @@ public class IdentityRoleManager : RoleManager, IDomainService throw new BusinessException(IdentityErrorCodes.StaticRoleDeletion); } - return await base.DeleteAsync(role); + var userIdList = await UserRepository.GetUserIdListByRoleIdAsync(role.Id, cancellationToken: CancellationToken); + var result = await base.DeleteAsync(role); + if (result.Succeeded) + { + Logger.LogDebug($"Remove dynamic claims cache for users of role: {role.Id}"); + await DynamicClaimCache.RemoveManyAsync(userIdList.Select(userId => AbpDynamicClaimCacheItem.CalculateCacheKey(userId, role.TenantId)), token: CancellationToken); + } + + return result; } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs index 57b97bfcd7..9432923785 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs @@ -7,12 +7,14 @@ using JetBrains.Annotations; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Volo.Abp.Caching; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Services; using Volo.Abp.EventBus.Distributed; using Volo.Abp.EventBus.Local; using Volo.Abp.Identity.Settings; +using Volo.Abp.Security.Claims; using Volo.Abp.Settings; using Volo.Abp.Threading; using Volo.Abp.Uow; @@ -28,6 +30,7 @@ public class IdentityUserManager : UserManager, IDomainService protected ICancellationTokenProvider CancellationTokenProvider { get; } protected IDistributedEventBus DistributedEventBus { get; } protected IIdentityLinkUserRepository IdentityLinkUserRepository { get; } + protected IDistributedCache DynamicClaimCache { get; } protected override CancellationToken CancellationToken => CancellationTokenProvider.Token; public IdentityUserManager( @@ -46,7 +49,8 @@ public class IdentityUserManager : UserManager, IDomainService IOrganizationUnitRepository organizationUnitRepository, ISettingProvider settingProvider, IDistributedEventBus distributedEventBus, - IIdentityLinkUserRepository identityLinkUserRepository) + IIdentityLinkUserRepository identityLinkUserRepository, + IDistributedCache dynamicClaimCache) : base( store, optionsAccessor, @@ -64,6 +68,7 @@ public class IdentityUserManager : UserManager, IDomainService RoleRepository = roleRepository; UserRepository = userRepository; IdentityLinkUserRepository = identityLinkUserRepository; + DynamicClaimCache = dynamicClaimCache; CancellationTokenProvider = cancellationTokenProvider; } @@ -160,6 +165,8 @@ public class IdentityUserManager : UserManager, IDomainService user.AddOrganizationUnit(ou.Id); await UserRepository.UpdateAsync(user, cancellationToken: CancellationToken); + + await DynamicClaimCache.RemoveAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(user.Id, user.TenantId), token: CancellationToken); } public virtual async Task RemoveFromOrganizationUnitAsync(Guid userId, Guid ouId) @@ -350,4 +357,42 @@ public class IdentityUserManager : UserManager, IDomainService return result; } + + public virtual async Task UpdateRoleAsync(Guid sourceRoleId, Guid? targetRoleId) + { + var sourceRole = await RoleRepository.GetAsync(sourceRoleId, cancellationToken: CancellationToken); + + Logger.LogDebug($"Remove dynamic claims cache for users of role: {sourceRoleId}"); + var userIdList = await UserRepository.GetUserIdListByRoleIdAsync(sourceRoleId, cancellationToken: CancellationToken); + await DynamicClaimCache.RemoveManyAsync(userIdList.Select(userId => AbpDynamicClaimCacheItem.CalculateCacheKey(userId, sourceRole.TenantId)), token: CancellationToken); + + var targetRole = targetRoleId.HasValue ? await RoleRepository.GetAsync(targetRoleId.Value, cancellationToken: CancellationToken) : null; + if (targetRole != null) + { + Logger.LogDebug($"Remove dynamic claims cache for users of role: {targetRoleId}"); + userIdList = await UserRepository.GetUserIdListByRoleIdAsync(targetRoleId.Value, cancellationToken: CancellationToken); + await DynamicClaimCache.RemoveManyAsync(userIdList.Select(userId => AbpDynamicClaimCacheItem.CalculateCacheKey(userId, targetRole.TenantId)), token: CancellationToken); + } + + await UserRepository.UpdateRoleAsync(sourceRoleId, targetRoleId, CancellationToken); + } + + public virtual async Task UpdateOrganizationAsync(Guid sourceOrganizationId, Guid? targetOrganizationId) + { + var sourceOrganization = await OrganizationUnitRepository.GetAsync(sourceOrganizationId, cancellationToken: CancellationToken); + + Logger.LogDebug($"Remove dynamic claims cache for users of organization: {sourceOrganizationId}"); + var userIdList = await OrganizationUnitRepository.GetMemberIdsAsync(sourceOrganizationId, cancellationToken: CancellationToken); + await DynamicClaimCache.RemoveManyAsync(userIdList.Select(userId => AbpDynamicClaimCacheItem.CalculateCacheKey(userId, sourceOrganization.TenantId)), token: CancellationToken); + + var targetOrganization = targetOrganizationId.HasValue ? await OrganizationUnitRepository.GetAsync(targetOrganizationId.Value, cancellationToken: CancellationToken) : null; + if (targetOrganization != null) + { + Logger.LogDebug($"Remove dynamic claims cache for users of organization: {targetOrganizationId}"); + userIdList = await OrganizationUnitRepository.GetMemberIdsAsync(targetOrganizationId.Value, cancellationToken: CancellationToken); + await DynamicClaimCache.RemoveManyAsync(userIdList.Select(userId => AbpDynamicClaimCacheItem.CalculateCacheKey(userId, targetOrganization.TenantId)), token: CancellationToken); + } + + await UserRepository.UpdateOrganizationAsync(sourceOrganizationId, targetOrganizationId, CancellationToken); + } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/OrganizationUnitManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/OrganizationUnitManager.cs index 1beb15f0b1..cf4c58107b 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/OrganizationUnitManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/OrganizationUnitManager.cs @@ -3,8 +3,11 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Volo.Abp.Caching; using Volo.Abp.Domain.Services; using Volo.Abp.Identity.Localization; +using Volo.Abp.Security.Claims; using Volo.Abp.Threading; using Volo.Abp.Uow; @@ -18,17 +21,20 @@ public class OrganizationUnitManager : DomainService protected IOrganizationUnitRepository OrganizationUnitRepository { get; } protected IStringLocalizer Localizer { get; } protected IIdentityRoleRepository IdentityRoleRepository { get; } + protected IDistributedCache DynamicClaimCache { get; } protected ICancellationTokenProvider CancellationTokenProvider { get; } public OrganizationUnitManager( IOrganizationUnitRepository organizationUnitRepository, IStringLocalizer localizer, IIdentityRoleRepository identityRoleRepository, + IDistributedCache dynamicClaimCache, ICancellationTokenProvider cancellationTokenProvider) { OrganizationUnitRepository = organizationUnitRepository; Localizer = localizer; IdentityRoleRepository = identityRoleRepository; + DynamicClaimCache = dynamicClaimCache; CancellationTokenProvider = cancellationTokenProvider; } @@ -44,6 +50,7 @@ public class OrganizationUnitManager : DomainService { await ValidateOrganizationUnitAsync(organizationUnit); await OrganizationUnitRepository.UpdateAsync(organizationUnit); + await RemoveDynamicClaimCacheAsync(organizationUnit); } public virtual async Task GetNextChildCodeAsync(Guid? parentId) @@ -84,6 +91,7 @@ public class OrganizationUnitManager : DomainService var organizationUnit = await OrganizationUnitRepository.GetAsync(id); + await RemoveDynamicClaimCacheAsync(organizationUnit); await OrganizationUnitRepository.RemoveAllMembersAsync(organizationUnit); await OrganizationUnitRepository.RemoveAllRolesAsync(organizationUnit); await OrganizationUnitRepository.DeleteAsync(id); @@ -169,16 +177,17 @@ public class OrganizationUnitManager : DomainService ); } - public virtual Task AddRoleToOrganizationUnitAsync(IdentityRole role, OrganizationUnit ou) + public virtual async Task AddRoleToOrganizationUnitAsync(IdentityRole role, OrganizationUnit ou) { var currentRoles = ou.Roles; if (currentRoles.Any(r => r.OrganizationUnitId == ou.Id && r.RoleId == role.Id)) { - return Task.FromResult(0); + return; } ou.AddRole(role.Id); - return OrganizationUnitRepository.UpdateAsync(ou); + await OrganizationUnitRepository.UpdateAsync(ou); + await RemoveDynamicClaimCacheAsync(ou); } public virtual async Task RemoveRoleFromOrganizationUnitAsync(Guid roleId, Guid ouId) @@ -189,9 +198,17 @@ public class OrganizationUnitManager : DomainService ); } - public virtual Task RemoveRoleFromOrganizationUnitAsync(IdentityRole role, OrganizationUnit organizationUnit) + public virtual async Task RemoveRoleFromOrganizationUnitAsync(IdentityRole role, OrganizationUnit organizationUnit) { organizationUnit.RemoveRole(role.Id); - return OrganizationUnitRepository.UpdateAsync(organizationUnit); + await OrganizationUnitRepository.UpdateAsync(organizationUnit); + await RemoveDynamicClaimCacheAsync(organizationUnit); + } + + protected virtual async Task RemoveDynamicClaimCacheAsync(OrganizationUnit organizationUnit) + { + Logger.LogDebug($"Remove dynamic claims cache for users of organization: {organizationUnit.Id}"); + var userIds = await OrganizationUnitRepository.GetMemberIdsAsync(organizationUnit.Id); + await DynamicClaimCache.RemoveManyAsync(userIds.Select(userId => AbpDynamicClaimCacheItem.CalculateCacheKey(userId, organizationUnit.TenantId))); } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserEntityUpdatedOrDeletedEventHandler.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserEntityUpdatedOrDeletedEventHandler.cs new file mode 100644 index 0000000000..a140d22f66 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserEntityUpdatedOrDeletedEventHandler.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.EventBus; +using Volo.Abp.Security.Claims; +using Volo.Abp.Uow; + +namespace Volo.Abp.Identity; + +public class UserEntityUpdatedOrDeletedEventHandler : + ILocalEventHandler>, + ILocalEventHandler>, + ITransientDependency +{ + public ILogger Logger { get; set; } + + private readonly IDistributedCache _dynamicClaimCache; + + public UserEntityUpdatedOrDeletedEventHandler(IDistributedCache dynamicClaimCache) + { + Logger = NullLogger.Instance; + + _dynamicClaimCache = dynamicClaimCache; + } + + [UnitOfWork] + public virtual async Task HandleEventAsync(EntityUpdatedEventData eventData) + { + await RemoveDynamicClaimCacheAsync(eventData.Entity.Id, eventData.Entity.TenantId); + } + + [UnitOfWork] + public virtual async Task HandleEventAsync(EntityDeletedEventData eventData) + { + await RemoveDynamicClaimCacheAsync(eventData.Entity.Id, eventData.Entity.TenantId); + } + + protected virtual async Task RemoveDynamicClaimCacheAsync(Guid userId, Guid? tenantId) + { + Logger.LogDebug($"Remove dynamic claims cache for user: {userId}"); + await _dynamicClaimCache.RemoveAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId)); + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserUpdatedEventHandler.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserUpdatedEventHandler.cs deleted file mode 100644 index 9ac8ad4746..0000000000 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserUpdatedEventHandler.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Domain.Entities.Events; -using Volo.Abp.EventBus; -using Volo.Abp.Uow; - -namespace Volo.Abp.Identity; - -public class UserEntityUpdatedEventHandler : - ILocalEventHandler>, - ILocalEventHandler>, - ITransientDependency -{ - private readonly IdentityDynamicClaimsPrincipalContributorCache _cache; - - public UserEntityUpdatedEventHandler(IdentityDynamicClaimsPrincipalContributorCache cache) - { - _cache = cache; - } - - [UnitOfWork] - public virtual async Task HandleEventAsync(EntityUpdatedEventData eventData) - { - await ClearAsync(eventData.Entity.Id, eventData.Entity.TenantId); - } - - [UnitOfWork] - public virtual async Task HandleEventAsync(EntityDeletedEventData eventData) - { - await ClearAsync(eventData.Entity.Id, eventData.Entity.TenantId); - } - - protected virtual async Task ClearAsync(Guid userId, Guid? tenantId) - { - await _cache.ClearAsync(userId, tenantId); - } -} diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs index 6f3eb540f2..0a91959f86 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs @@ -151,6 +151,12 @@ public class EfCoreIdentityUserRepository : EfCoreRepository> GetUserIdListByRoleIdAsync(Guid roleId, CancellationToken cancellationToken = default) + { + return await (await GetDbContextAsync()).Set().Where(x => x.RoleId == roleId) + .Select(x => x.UserId).ToListAsync(GetCancellationToken(cancellationToken)); + } + public virtual async Task> GetListAsync( string sorting = null, int maxResultCount = int.MaxValue, diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs index 54ceaa92b6..fa4b0c7b8d 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs @@ -192,6 +192,16 @@ public class EfCoreOrganizationUnitRepository .ToListAsync(GetCancellationToken(cancellationToken)); } + public virtual async Task> GetMemberIdsAsync(Guid id, CancellationToken cancellationToken = default) + { + var dbContext = await GetDbContextAsync(); + + return await (from userOu in dbContext.Set() + join user in dbContext.Users on userOu.UserId equals user.Id + where userOu.OrganizationUnitId == id + select user.Id).ToListAsync(cancellationToken); + } + public virtual async Task GetMembersCountAsync( OrganizationUnit organizationUnit, string filter = null, diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs index 255e492c05..935367c5c3 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs @@ -131,7 +131,17 @@ public class MongoIdentityUserRepository : MongoDbRepository u.Roles.Any(r => r.RoleId == role.Id)) .ToListAsync(cancellationToken); } - + + public virtual async Task> GetUserIdListByRoleIdAsync(Guid roleId, CancellationToken cancellationToken = default) + { + cancellationToken = GetCancellationToken(cancellationToken); + + return await (await GetMongoQueryableAsync(cancellationToken)) + .Where(u => u.Roles.Any(r => r.RoleId == roleId)) + .Select(x => x.Id) + .ToListAsync(cancellationToken); + } + public virtual async Task> GetListAsync( string sorting = null, int maxResultCount = int.MaxValue, @@ -270,7 +280,7 @@ public class MongoIdentityUserRepository : MongoDbRepository>(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime) .LongCountAsync(GetCancellationToken(cancellationToken)); } - + public virtual async Task> GetUsersInOrganizationUnitAsync( Guid organizationUnitId, CancellationToken cancellationToken = default) @@ -301,7 +311,7 @@ public class MongoIdentityUserRepository : MongoDbRepository ou.Code.StartsWith(code)) .Select(ou => ou.Id) .ToListAsync(cancellationToken); - + return await (await GetMongoQueryableAsync(cancellationToken)) .Where(u => u.OrganizationUnits.Any(uou => organizationUnitIds.Contains(uou.OrganizationUnitId))) .ToListAsync(cancellationToken); @@ -364,29 +374,29 @@ public class MongoIdentityUserRepository : MongoDbRepository> GetRoleNamesAsync( - IEnumerable userIds, + IEnumerable userIds, CancellationToken cancellationToken = default) { cancellationToken = GetCancellationToken(cancellationToken); - + var userAndRoleIds = (await GetMongoQueryableAsync(cancellationToken)) .Where(u => userIds.Contains(u.Id)) .SelectMany(u => u.Roles) - .Select(userRole => new + .Select(userRole => new { userRole.UserId, userRole.RoleId }).GroupBy(x => x.UserId).ToDictionary(x => x.Key, x => x.Select(r => r.RoleId).ToList()); var roleIds = userAndRoleIds.SelectMany(x => x.Value); - var roles = await (await GetMongoQueryableAsync(cancellationToken)).Where(r => roleIds.Contains(r.Id)).Select(r => new + var roles = await (await GetMongoQueryableAsync(cancellationToken)).Where(r => roleIds.Contains(r.Id)).Select(r => new { r.Id, r.Name }).ToListAsync(cancellationToken); - + var result = userAndRoleIds.ToDictionary(x => x.Key, x => roles.Where(r => x.Value.Contains(r.Id)).Select(r => r.Name).ToArray()); - + return result.Select(x => new IdentityUserIdWithRoleNames() { Id = x.Key, RoleNames = x.Value }).ToList(); } } diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoOrganizationUnitRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoOrganizationUnitRepository.cs index 3ebbc6f550..77b9b3aa98 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoOrganizationUnitRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoOrganizationUnitRepository.cs @@ -181,6 +181,14 @@ public class MongoOrganizationUnitRepository .ToListAsync(cancellationToken); } + public virtual async Task> GetMemberIdsAsync(Guid id, CancellationToken cancellationToken = default) + { + cancellationToken = GetCancellationToken(cancellationToken); + return await (await GetMongoQueryableAsync(cancellationToken)) + .Where(u => u.OrganizationUnits.Any(uou => uou.OrganizationUnitId == id)).Select(x => x.Id) + .ToListAsync(cancellationToken); + } + public virtual async Task GetMembersCountAsync( OrganizationUnit organizationUnit, string filter = null, diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributor_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributor_Tests.cs index 9ac0a2d36b..0188ce2cc1 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributor_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributor_Tests.cs @@ -1,3 +1,4 @@ +using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Shouldly; @@ -9,6 +10,10 @@ namespace Volo.Abp.Identity; public class IdentityDynamicClaimsPrincipalContributor_Tests : AbpIdentityDomainTestBase { private readonly IdentityUserManager _identityUserManager; + private readonly IIdentityRoleRepository _identityRoleRepository; + private readonly IdentityRoleManager _identityRoleManager; + private readonly IOrganizationUnitRepository _organizationUnitRepository; + private readonly OrganizationUnitManager _organizationUnitManager; private readonly IAbpClaimsPrincipalFactory _abpClaimsPrincipalFactory; private readonly AbpUserClaimsPrincipalFactory _abpUserClaimsPrincipalFactory; private readonly IdentityTestData _testData; @@ -16,13 +21,17 @@ public class IdentityDynamicClaimsPrincipalContributor_Tests : AbpIdentityDomain public IdentityDynamicClaimsPrincipalContributor_Tests() { _identityUserManager = GetRequiredService(); + _identityRoleRepository = GetRequiredService(); + _identityRoleManager = GetRequiredService(); + _organizationUnitRepository = GetRequiredService(); + _organizationUnitManager = GetRequiredService(); _abpClaimsPrincipalFactory = GetRequiredService(); _abpUserClaimsPrincipalFactory = GetRequiredService(); _testData = GetRequiredService(); } [Fact] - public async Task Should_Get_Correct_Claims_After_User_Updating() + public async Task Should_Get_Correct_Claims_After_User_Updated() { IdentityUser user = null; ClaimsPrincipal claimsPrincipal = null; @@ -38,12 +47,18 @@ public class IdentityDynamicClaimsPrincipalContributor_Tests : AbpIdentityDomain claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Name && x.Value == user.UserName); claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Email && x.Value == user.Email); claimsPrincipal.Claims.ShouldContain(x => x.Type == "AspNet.Identity.SecurityStamp" && x.Value == securityStamp); + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "supporter"); + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "moderator"); + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager"); var dynamicClaimsPrincipal = await _abpClaimsPrincipalFactory.CreateDynamicAsync(claimsPrincipal); dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.NameIdentifier && x.Value == user.Id.ToString()); dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Name && x.Value == user.UserName); dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Email && x.Value == user.Email); dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == "AspNet.Identity.SecurityStamp" && x.Value == securityStamp);//SecurityStamp is not dynamic claim + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "supporter"); + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "moderator"); + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager"); await _identityUserManager.SetUserNameAsync(user, "newUserName"); await _identityUserManager.SetEmailAsync(user, "newUserEmail@abp.io"); @@ -55,5 +70,143 @@ public class IdentityDynamicClaimsPrincipalContributor_Tests : AbpIdentityDomain dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Name && x.Value =="newUserName"); dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Email && x.Value == "newUserEmail@abp.io"); dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == "AspNet.Identity.SecurityStamp" && x.Value == securityStamp);//SecurityStamp is not dynamic claim + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "supporter"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "moderator"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager"); + } + + [Fact] + public async Task Should_Get_Correct_Claims_After_User_Role_Updated() + { + ClaimsPrincipal claimsPrincipal = null; + await UsingUowAsync(async () => + { + var user = await _identityUserManager.GetByIdAsync(_testData.UserJohnId); + user.ShouldNotBeNull(); + claimsPrincipal = await _abpUserClaimsPrincipalFactory.CreateAsync(user); + + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "supporter"); + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "moderator"); + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager"); + + var dynamicClaimsPrincipal = await _abpClaimsPrincipalFactory.CreateDynamicAsync(claimsPrincipal); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "supporter"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "moderator"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager"); + + var roles = (await _identityRoleRepository.GetListAsync()).Where(x => user.Roles.Select(r => r.RoleId).Contains(x.Id)).ToList(); + + var role = roles.First(x => x.Name == "supporter"); + await _identityRoleManager.SetRoleNameAsync(role, "newSupporter"); + await _identityRoleRepository.UpdateAsync(role); + }); + + var dynamicClaimsPrincipal = await _abpClaimsPrincipalFactory.CreateDynamicAsync(claimsPrincipal); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "newSupporter"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "moderator"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager"); + } + + [Fact] + public async Task Should_Get_Correct_Claims_After_User_Role_Deleted() + { + ClaimsPrincipal claimsPrincipal = null; + await UsingUowAsync(async () => + { + var user = await _identityUserManager.GetByIdAsync(_testData.UserJohnId); + user.ShouldNotBeNull(); + claimsPrincipal = await _abpUserClaimsPrincipalFactory.CreateAsync(user); + + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "supporter"); + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "moderator"); + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager"); + + var dynamicClaimsPrincipal = await _abpClaimsPrincipalFactory.CreateDynamicAsync(claimsPrincipal); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "supporter"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "moderator"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager"); + + var roles = (await _identityRoleRepository.GetListAsync()).Where(x => user.Roles.Select(r => r.RoleId).Contains(x.Id)).ToList(); + + await _identityRoleManager.DeleteAsync(roles.First(x => x.Name == "supporter")); + await _identityRoleManager.DeleteAsync(roles.First(x => x.Name == "moderator")); + }); + + var dynamicClaimsPrincipal = await _abpClaimsPrincipalFactory.CreateDynamicAsync(claimsPrincipal); + dynamicClaimsPrincipal.Claims.ShouldNotContain(x => x.Type == ClaimTypes.Role && x.Value == "supporter"); + dynamicClaimsPrincipal.Claims.ShouldNotContain(x => x.Type == ClaimTypes.Role && x.Value == "moderator"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager"); + } + + [Fact] + public async Task Should_Get_Correct_Claims_After_User_Organization_Updated() + { + ClaimsPrincipal claimsPrincipal = null; + await UsingUowAsync(async () => + { + var user = await _identityUserManager.GetByIdAsync(_testData.UserJohnId); + user.ShouldNotBeNull(); + claimsPrincipal = await _abpUserClaimsPrincipalFactory.CreateAsync(user); + + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "supporter"); + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "moderator"); + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager"); + + var dynamicClaimsPrincipal = await _abpClaimsPrincipalFactory.CreateDynamicAsync(claimsPrincipal); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "supporter"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "moderator"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager"); + + var ou = await _organizationUnitRepository.GetAsync("OU111", true); + ou.ShouldNotBeNull(); + ou.Roles.Count.ShouldBe(2); + ou.Roles.ShouldContain(x => x.RoleId == _testData.RoleModeratorId); + ou.Roles.ShouldContain(x => x.RoleId == _testData.RoleManagerId); + + ou.AddRole(_testData.RoleSaleId); + await _organizationUnitManager.UpdateAsync(ou); + }); + + var dynamicClaimsPrincipal = await _abpClaimsPrincipalFactory.CreateDynamicAsync(claimsPrincipal); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "supporter"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "moderator"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "sale"); + } + + [Fact] + public async Task Should_Get_Correct_Claims_After_User_Organization_Deleted() + { + ClaimsPrincipal claimsPrincipal = null; + await UsingUowAsync(async () => + { + var user = await _identityUserManager.GetByIdAsync(_testData.UserJohnId); + user.ShouldNotBeNull(); + claimsPrincipal = await _abpUserClaimsPrincipalFactory.CreateAsync(user); + + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "supporter"); + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "moderator"); + claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager"); + + var dynamicClaimsPrincipal = await _abpClaimsPrincipalFactory.CreateDynamicAsync(claimsPrincipal); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "supporter"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "moderator"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "manager"); + + var ou = await _organizationUnitRepository.GetAsync("OU111", true); + ou.ShouldNotBeNull(); + ou.Roles.Count.ShouldBe(2); + ou.Roles.ShouldContain(x => x.RoleId == _testData.RoleModeratorId); + ou.Roles.ShouldContain(x => x.RoleId == _testData.RoleManagerId); + var users = await _organizationUnitRepository.GetMemberIdsAsync(ou.Id); + users.ShouldContain(user.Id); + + await _organizationUnitManager.DeleteAsync(ou.Id); + }); + + var dynamicClaimsPrincipal = await _abpClaimsPrincipalFactory.CreateDynamicAsync(claimsPrincipal); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "supporter"); + dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Role && x.Value == "moderator"); + dynamicClaimsPrincipal.Claims.ShouldNotContain(x => x.Type == ClaimTypes.Role && x.Value == "manager"); //manager role from OU111 is deleted. } } diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs index 7b5ffd259b..e30e1d3bfe 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs @@ -83,8 +83,11 @@ public class AbpIdentityTestDataBuilder : ITransientDependency _supporterRole = new IdentityRole(_testData.RoleSupporterId, "supporter"); await _roleRepository.InsertAsync(_supporterRole); - _managerRole = new IdentityRole(_guidGenerator.Create(), "manager"); + _managerRole = new IdentityRole(_testData.RoleManagerId, "manager"); await _roleRepository.InsertAsync(_managerRole); + + var saleRole = new IdentityRole(_testData.RoleSaleId, "sale"); + await _roleRepository.InsertAsync(saleRole); } /* Creates OU tree as shown below: diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityRoleRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityRoleRepository_Tests.cs index c34e9ad56c..33f5eeade9 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityRoleRepository_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityRoleRepository_Tests.cs @@ -66,16 +66,17 @@ public abstract class IdentityRoleRepository_Tests : AbpIdentity role.Claims.ShouldNotBeNull(); role.Claims.Any().ShouldBeTrue(); } - + [Fact] public async Task GetListWithUserCountAsync() { var roles = await RoleRepository.GetListWithUserCountAsync(); - roles.Count.ShouldBe(4); + roles.Count.ShouldBe(5); roles.ShouldContain(r => r.Role.Name == "admin" && r.UserCount == 2); roles.ShouldContain(r => r.Role.Name == "moderator" && r.UserCount == 1); roles.ShouldContain(r => r.Role.Name == "supporter" && r.UserCount == 2); roles.ShouldContain(r => r.Role.Name == "manager" && r.UserCount == 1); + roles.ShouldContain(r => r.Role.Name == "sale" && r.UserCount == 0); } } diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityTestData.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityTestData.cs index 4862450e35..0172dc016a 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityTestData.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityTestData.cs @@ -6,9 +6,9 @@ namespace Volo.Abp.Identity; public class IdentityTestData : ISingletonDependency { public Guid RoleModeratorId { get; } = Guid.NewGuid(); - public Guid RoleSupporterId { get; } = Guid.NewGuid(); - + public Guid RoleManagerId { get; } = Guid.NewGuid(); + public Guid RoleSaleId { get; } = Guid.NewGuid(); public Guid UserJohnId { get; } = Guid.NewGuid(); public Guid UserDavidId { get; } = Guid.NewGuid(); public Guid UserNeoId { get; } = Guid.NewGuid(); diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs index 2d2c354aae..9132a1e023 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs @@ -14,6 +14,7 @@ public abstract class IdentityUserRepository_Tests : AbpIdentity where TStartupModule : IAbpModule { protected IIdentityUserRepository UserRepository { get; } + protected IIdentityRoleRepository RoleRepository { get; } protected ILookupNormalizer LookupNormalizer { get; } protected IOrganizationUnitRepository OrganizationUnitRepository { get; } protected OrganizationUnitManager OrganizationUnitManager { get; } @@ -22,6 +23,7 @@ public abstract class IdentityUserRepository_Tests : AbpIdentity protected IdentityUserRepository_Tests() { UserRepository = GetRequiredService(); + RoleRepository = GetRequiredService(); LookupNormalizer = GetRequiredService(); OrganizationUnitRepository = GetRequiredService(); OrganizationUnitManager = GetRequiredService();; @@ -53,7 +55,7 @@ public abstract class IdentityUserRepository_Tests : AbpIdentity roles.ShouldContain("supporter"); roles.ShouldContain("manager"); } - + [Fact] public async Task GetRoleNames_By_UserIds_Async() { @@ -63,18 +65,18 @@ public abstract class IdentityUserRepository_Tests : AbpIdentity TestData.UserNeoId, TestData.UserDavidId }); - + userRoleNames.Count.ShouldBe(3); - + var userBob = userRoleNames.First(x => x.Id == TestData.UserBobId); userBob.RoleNames.Length.ShouldBe(1); userBob.RoleNames[0].ShouldBe("manager"); - + var userJohn = userRoleNames.First(x => x.Id == TestData.UserJohnId); userJohn.RoleNames.Length.ShouldBe(2); userJohn.RoleNames.ShouldContain("moderator"); userJohn.RoleNames.ShouldContain("supporter"); - + var userNeo = userRoleNames.First(x => x.Id == TestData.UserNeoId); userNeo.RoleNames.Length.ShouldBe(1); userNeo.RoleNames[0].ShouldBe("supporter"); @@ -121,6 +123,21 @@ public abstract class IdentityUserRepository_Tests : AbpIdentity users.ShouldContain(u => u.UserName == "neo"); } + [Fact] + public async Task GetUserIdListByRoleIdAsync() + { + var john = await UserRepository.FindByNormalizedUserNameAsync(LookupNormalizer.NormalizeName("john.nash")); + var neo = await UserRepository.FindByNormalizedUserNameAsync(LookupNormalizer.NormalizeName("neo")); + john.ShouldNotBeNull(); + neo.ShouldNotBeNull(); + + var roleId = (await RoleRepository.FindByNormalizedNameAsync(LookupNormalizer.NormalizeName("supporter"))).Id; + var users = await UserRepository.GetUserIdListByRoleIdAsync(roleId); + users.Count.ShouldBe(2); + users.ShouldContain(id => id == john.Id); + users.ShouldContain(id => id == neo.Id); + } + [Fact] public async Task GetListAsync() { diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs index 064d543269..8803343d8c 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs @@ -232,6 +232,17 @@ public abstract class OrganizationUnitRepository_Tests : AbpIden users.Count.ShouldBe(0); } + [Fact] + public async Task GetMemberIdsAsync() + { + var ou = await _organizationUnitRepository.GetAsync("OU111"); + var users = await _organizationUnitRepository.GetMemberIdsAsync(ou.Id); + + users.Count.ShouldBe(2); + users.ShouldContain(x => x == _testData.UserJohnId); + users.ShouldContain(x => x == _testData.UserNeoId); + } + [Fact] public async Task GetMembersCountOfOrganizationUnit() {