Browse Source

Clear Dynamic claims cache when role or organization are changed.

pull/18200/head
maliming 3 years ago
parent
commit
3f2d1fd7a8
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 5
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs
  2. 5
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs
  3. 14
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributorCache.cs
  4. 30
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleManager.cs
  5. 47
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs
  6. 27
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/OrganizationUnitManager.cs
  7. 47
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserEntityUpdatedOrDeletedEventHandler.cs
  8. 40
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserUpdatedEventHandler.cs
  9. 6
      modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs
  10. 10
      modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs
  11. 28
      modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs
  12. 8
      modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoOrganizationUnitRepository.cs
  13. 155
      modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributor_Tests.cs
  14. 5
      modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs
  15. 5
      modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityRoleRepository_Tests.cs
  16. 4
      modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityTestData.cs
  17. 27
      modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs
  18. 11
      modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs

5
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs

@ -50,6 +50,11 @@ public interface IIdentityUserRepository : IBasicRepository<IdentityUser, Guid>
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
); );
Task<List<Guid>> GetUserIdListByRoleIdAsync(
Guid roleId,
CancellationToken cancellationToken = default
);
Task<List<IdentityUser>> GetListAsync( Task<List<IdentityUser>> GetListAsync(
string sorting = null, string sorting = null,
int maxResultCount = int.MaxValue, int maxResultCount = int.MaxValue,

5
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs

@ -90,6 +90,11 @@ public interface IOrganizationUnitRepository : IBasicRepository<OrganizationUnit
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
); );
Task<List<Guid>> GetMemberIdsAsync(
Guid id,
CancellationToken cancellationToken = default
);
Task<int> GetMembersCountAsync( Task<int> GetMembersCountAsync(
OrganizationUnit organizationUnit, OrganizationUnit organizationUnit,
string filter = null, string filter = null,

14
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDynamicClaimsPrincipalContributorCache.cs

@ -18,7 +18,7 @@ public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependen
{ {
public ILogger<IdentityDynamicClaimsPrincipalContributorCache> Logger { get; set; } public ILogger<IdentityDynamicClaimsPrincipalContributorCache> Logger { get; set; }
protected IDistributedCache<AbpDynamicClaimCacheItem> Cache { get; } protected IDistributedCache<AbpDynamicClaimCacheItem> DynamicClaimCache { get; }
protected ICurrentTenant CurrentTenant { get; } protected ICurrentTenant CurrentTenant { get; }
protected IdentityUserManager UserManager { get; } protected IdentityUserManager UserManager { get; }
protected IUserClaimsPrincipalFactory<IdentityUser> UserClaimsPrincipalFactory { get; } protected IUserClaimsPrincipalFactory<IdentityUser> UserClaimsPrincipalFactory { get; }
@ -26,14 +26,14 @@ public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependen
protected IOptions<IdentityDynamicClaimsPrincipalContributorCacheOptions> CacheOptions { get; } protected IOptions<IdentityDynamicClaimsPrincipalContributorCacheOptions> CacheOptions { get; }
public IdentityDynamicClaimsPrincipalContributorCache( public IdentityDynamicClaimsPrincipalContributorCache(
IDistributedCache<AbpDynamicClaimCacheItem> cache, IDistributedCache<AbpDynamicClaimCacheItem> dynamicClaimCache,
ICurrentTenant currentTenant, ICurrentTenant currentTenant,
IdentityUserManager userManager, IdentityUserManager userManager,
IUserClaimsPrincipalFactory<IdentityUser> userClaimsPrincipalFactory, IUserClaimsPrincipalFactory<IdentityUser> userClaimsPrincipalFactory,
IOptions<AbpClaimsPrincipalFactoryOptions> abpClaimsPrincipalFactoryOptions, IOptions<AbpClaimsPrincipalFactoryOptions> abpClaimsPrincipalFactoryOptions,
IOptions<IdentityDynamicClaimsPrincipalContributorCacheOptions> cacheOptions) IOptions<IdentityDynamicClaimsPrincipalContributorCacheOptions> cacheOptions)
{ {
Cache = cache; DynamicClaimCache = dynamicClaimCache;
CurrentTenant = currentTenant; CurrentTenant = currentTenant;
UserManager = userManager; UserManager = userManager;
UserClaimsPrincipalFactory = userClaimsPrincipalFactory; UserClaimsPrincipalFactory = userClaimsPrincipalFactory;
@ -50,7 +50,7 @@ public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependen
if (AbpClaimsPrincipalFactoryOptions.Value.DynamicClaims.IsNullOrEmpty()) if (AbpClaimsPrincipalFactoryOptions.Value.DynamicClaims.IsNullOrEmpty())
{ {
var emptyCacheItem = new AbpDynamicClaimCacheItem(); 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 AbsoluteExpirationRelativeToNow = CacheOptions.Value.CacheAbsoluteExpiration
}); });
@ -58,7 +58,7 @@ public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependen
return emptyCacheItem; return emptyCacheItem;
} }
return await Cache.GetOrAddAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId), async () => return await DynamicClaimCache.GetOrAddAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId), async () =>
{ {
using (CurrentTenant.Change(tenantId)) using (CurrentTenant.Change(tenantId))
{ {
@ -91,7 +91,7 @@ public class IdentityDynamicClaimsPrincipalContributorCache : ITransientDependen
public virtual async Task ClearAsync(Guid userId, Guid? tenantId = null) public virtual async Task ClearAsync(Guid userId, Guid? tenantId = null)
{ {
Logger.LogDebug($"Clearing dynamic claims cache for user: {userId}"); Logger.LogDebug($"Remove dynamic claims cache for user: {userId}");
await Cache.RemoveAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId)); await DynamicClaimCache.RemoveAsync(AbpDynamicClaimCacheItem.CalculateCacheKey(userId, tenantId));
} }
} }

30
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.AspNetCore.Identity;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Volo.Abp.Caching;
using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Services; using Volo.Abp.Domain.Services;
using Volo.Abp.Identity.Localization; using Volo.Abp.Identity.Localization;
using Volo.Abp.Security.Claims;
using Volo.Abp.Threading; using Volo.Abp.Threading;
namespace Volo.Abp.Identity; namespace Volo.Abp.Identity;
@ -19,6 +21,8 @@ public class IdentityRoleManager : RoleManager<IdentityRole>, IDomainService
protected IStringLocalizer<IdentityResource> Localizer { get; } protected IStringLocalizer<IdentityResource> Localizer { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; } protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected IIdentityUserRepository UserRepository { get; }
protected IDistributedCache<AbpDynamicClaimCacheItem> DynamicClaimCache { get; }
public IdentityRoleManager( public IdentityRoleManager(
IdentityRoleStore store, IdentityRoleStore store,
@ -27,7 +31,9 @@ public class IdentityRoleManager : RoleManager<IdentityRole>, IDomainService
IdentityErrorDescriber errors, IdentityErrorDescriber errors,
ILogger<IdentityRoleManager> logger, ILogger<IdentityRoleManager> logger,
IStringLocalizer<IdentityResource> localizer, IStringLocalizer<IdentityResource> localizer,
ICancellationTokenProvider cancellationTokenProvider) ICancellationTokenProvider cancellationTokenProvider,
IIdentityUserRepository userRepository,
IDistributedCache<AbpDynamicClaimCacheItem> dynamicClaimCache)
: base( : base(
store, store,
roleValidators, roleValidators,
@ -37,6 +43,8 @@ public class IdentityRoleManager : RoleManager<IdentityRole>, IDomainService
{ {
Localizer = localizer; Localizer = localizer;
CancellationTokenProvider = cancellationTokenProvider; CancellationTokenProvider = cancellationTokenProvider;
UserRepository = userRepository;
DynamicClaimCache = dynamicClaimCache;
} }
public virtual async Task<IdentityRole> GetByIdAsync(Guid id) public virtual async Task<IdentityRole> GetByIdAsync(Guid id)
@ -57,7 +65,15 @@ public class IdentityRoleManager : RoleManager<IdentityRole>, IDomainService
throw new BusinessException(IdentityErrorCodes.StaticRoleRenaming); 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<IdentityResult> DeleteAsync(IdentityRole role) public async override Task<IdentityResult> DeleteAsync(IdentityRole role)
@ -67,6 +83,14 @@ public class IdentityRoleManager : RoleManager<IdentityRole>, IDomainService
throw new BusinessException(IdentityErrorCodes.StaticRoleDeletion); 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;
} }
} }

47
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.AspNetCore.Identity;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.Caching;
using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Services; using Volo.Abp.Domain.Services;
using Volo.Abp.EventBus.Distributed; using Volo.Abp.EventBus.Distributed;
using Volo.Abp.EventBus.Local; using Volo.Abp.EventBus.Local;
using Volo.Abp.Identity.Settings; using Volo.Abp.Identity.Settings;
using Volo.Abp.Security.Claims;
using Volo.Abp.Settings; using Volo.Abp.Settings;
using Volo.Abp.Threading; using Volo.Abp.Threading;
using Volo.Abp.Uow; using Volo.Abp.Uow;
@ -28,6 +30,7 @@ public class IdentityUserManager : UserManager<IdentityUser>, IDomainService
protected ICancellationTokenProvider CancellationTokenProvider { get; } protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected IDistributedEventBus DistributedEventBus { get; } protected IDistributedEventBus DistributedEventBus { get; }
protected IIdentityLinkUserRepository IdentityLinkUserRepository { get; } protected IIdentityLinkUserRepository IdentityLinkUserRepository { get; }
protected IDistributedCache<AbpDynamicClaimCacheItem> DynamicClaimCache { get; }
protected override CancellationToken CancellationToken => CancellationTokenProvider.Token; protected override CancellationToken CancellationToken => CancellationTokenProvider.Token;
public IdentityUserManager( public IdentityUserManager(
@ -46,7 +49,8 @@ public class IdentityUserManager : UserManager<IdentityUser>, IDomainService
IOrganizationUnitRepository organizationUnitRepository, IOrganizationUnitRepository organizationUnitRepository,
ISettingProvider settingProvider, ISettingProvider settingProvider,
IDistributedEventBus distributedEventBus, IDistributedEventBus distributedEventBus,
IIdentityLinkUserRepository identityLinkUserRepository) IIdentityLinkUserRepository identityLinkUserRepository,
IDistributedCache<AbpDynamicClaimCacheItem> dynamicClaimCache)
: base( : base(
store, store,
optionsAccessor, optionsAccessor,
@ -64,6 +68,7 @@ public class IdentityUserManager : UserManager<IdentityUser>, IDomainService
RoleRepository = roleRepository; RoleRepository = roleRepository;
UserRepository = userRepository; UserRepository = userRepository;
IdentityLinkUserRepository = identityLinkUserRepository; IdentityLinkUserRepository = identityLinkUserRepository;
DynamicClaimCache = dynamicClaimCache;
CancellationTokenProvider = cancellationTokenProvider; CancellationTokenProvider = cancellationTokenProvider;
} }
@ -160,6 +165,8 @@ public class IdentityUserManager : UserManager<IdentityUser>, IDomainService
user.AddOrganizationUnit(ou.Id); user.AddOrganizationUnit(ou.Id);
await UserRepository.UpdateAsync(user, cancellationToken: CancellationToken); 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) public virtual async Task RemoveFromOrganizationUnitAsync(Guid userId, Guid ouId)
@ -350,4 +357,42 @@ public class IdentityUserManager : UserManager<IdentityUser>, IDomainService
return result; 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);
}
} }

27
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/OrganizationUnitManager.cs

@ -3,8 +3,11 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.Caching;
using Volo.Abp.Domain.Services; using Volo.Abp.Domain.Services;
using Volo.Abp.Identity.Localization; using Volo.Abp.Identity.Localization;
using Volo.Abp.Security.Claims;
using Volo.Abp.Threading; using Volo.Abp.Threading;
using Volo.Abp.Uow; using Volo.Abp.Uow;
@ -18,17 +21,20 @@ public class OrganizationUnitManager : DomainService
protected IOrganizationUnitRepository OrganizationUnitRepository { get; } protected IOrganizationUnitRepository OrganizationUnitRepository { get; }
protected IStringLocalizer<IdentityResource> Localizer { get; } protected IStringLocalizer<IdentityResource> Localizer { get; }
protected IIdentityRoleRepository IdentityRoleRepository { get; } protected IIdentityRoleRepository IdentityRoleRepository { get; }
protected IDistributedCache<AbpDynamicClaimCacheItem> DynamicClaimCache { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; } protected ICancellationTokenProvider CancellationTokenProvider { get; }
public OrganizationUnitManager( public OrganizationUnitManager(
IOrganizationUnitRepository organizationUnitRepository, IOrganizationUnitRepository organizationUnitRepository,
IStringLocalizer<IdentityResource> localizer, IStringLocalizer<IdentityResource> localizer,
IIdentityRoleRepository identityRoleRepository, IIdentityRoleRepository identityRoleRepository,
IDistributedCache<AbpDynamicClaimCacheItem> dynamicClaimCache,
ICancellationTokenProvider cancellationTokenProvider) ICancellationTokenProvider cancellationTokenProvider)
{ {
OrganizationUnitRepository = organizationUnitRepository; OrganizationUnitRepository = organizationUnitRepository;
Localizer = localizer; Localizer = localizer;
IdentityRoleRepository = identityRoleRepository; IdentityRoleRepository = identityRoleRepository;
DynamicClaimCache = dynamicClaimCache;
CancellationTokenProvider = cancellationTokenProvider; CancellationTokenProvider = cancellationTokenProvider;
} }
@ -44,6 +50,7 @@ public class OrganizationUnitManager : DomainService
{ {
await ValidateOrganizationUnitAsync(organizationUnit); await ValidateOrganizationUnitAsync(organizationUnit);
await OrganizationUnitRepository.UpdateAsync(organizationUnit); await OrganizationUnitRepository.UpdateAsync(organizationUnit);
await RemoveDynamicClaimCacheAsync(organizationUnit);
} }
public virtual async Task<string> GetNextChildCodeAsync(Guid? parentId) public virtual async Task<string> GetNextChildCodeAsync(Guid? parentId)
@ -84,6 +91,7 @@ public class OrganizationUnitManager : DomainService
var organizationUnit = await OrganizationUnitRepository.GetAsync(id); var organizationUnit = await OrganizationUnitRepository.GetAsync(id);
await RemoveDynamicClaimCacheAsync(organizationUnit);
await OrganizationUnitRepository.RemoveAllMembersAsync(organizationUnit); await OrganizationUnitRepository.RemoveAllMembersAsync(organizationUnit);
await OrganizationUnitRepository.RemoveAllRolesAsync(organizationUnit); await OrganizationUnitRepository.RemoveAllRolesAsync(organizationUnit);
await OrganizationUnitRepository.DeleteAsync(id); 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; var currentRoles = ou.Roles;
if (currentRoles.Any(r => r.OrganizationUnitId == ou.Id && r.RoleId == role.Id)) if (currentRoles.Any(r => r.OrganizationUnitId == ou.Id && r.RoleId == role.Id))
{ {
return Task.FromResult(0); return;
} }
ou.AddRole(role.Id); ou.AddRole(role.Id);
return OrganizationUnitRepository.UpdateAsync(ou); await OrganizationUnitRepository.UpdateAsync(ou);
await RemoveDynamicClaimCacheAsync(ou);
} }
public virtual async Task RemoveRoleFromOrganizationUnitAsync(Guid roleId, Guid ouId) 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); 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)));
} }
} }

47
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<EntityUpdatedEventData<IdentityUser>>,
ILocalEventHandler<EntityDeletedEventData<IdentityUser>>,
ITransientDependency
{
public ILogger<UserEntityUpdatedOrDeletedEventHandler> Logger { get; set; }
private readonly IDistributedCache<AbpDynamicClaimCacheItem> _dynamicClaimCache;
public UserEntityUpdatedOrDeletedEventHandler(IDistributedCache<AbpDynamicClaimCacheItem> dynamicClaimCache)
{
Logger = NullLogger<UserEntityUpdatedOrDeletedEventHandler>.Instance;
_dynamicClaimCache = dynamicClaimCache;
}
[UnitOfWork]
public virtual async Task HandleEventAsync(EntityUpdatedEventData<IdentityUser> eventData)
{
await RemoveDynamicClaimCacheAsync(eventData.Entity.Id, eventData.Entity.TenantId);
}
[UnitOfWork]
public virtual async Task HandleEventAsync(EntityDeletedEventData<IdentityUser> 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));
}
}

40
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/UserUpdatedEventHandler.cs

@ -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<EntityUpdatedEventData<IdentityUser>>,
ILocalEventHandler<EntityDeletedEventData<IdentityUser>>,
ITransientDependency
{
private readonly IdentityDynamicClaimsPrincipalContributorCache _cache;
public UserEntityUpdatedEventHandler(IdentityDynamicClaimsPrincipalContributorCache cache)
{
_cache = cache;
}
[UnitOfWork]
public virtual async Task HandleEventAsync(EntityUpdatedEventData<IdentityUser> eventData)
{
await ClearAsync(eventData.Entity.Id, eventData.Entity.TenantId);
}
[UnitOfWork]
public virtual async Task HandleEventAsync(EntityDeletedEventData<IdentityUser> eventData)
{
await ClearAsync(eventData.Entity.Id, eventData.Entity.TenantId);
}
protected virtual async Task ClearAsync(Guid userId, Guid? tenantId)
{
await _cache.ClearAsync(userId, tenantId);
}
}

6
modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs

@ -151,6 +151,12 @@ public class EfCoreIdentityUserRepository : EfCoreRepository<IIdentityDbContext,
.ToListAsync(GetCancellationToken(cancellationToken)); .ToListAsync(GetCancellationToken(cancellationToken));
} }
public virtual async Task<List<Guid>> GetUserIdListByRoleIdAsync(Guid roleId, CancellationToken cancellationToken = default)
{
return await (await GetDbContextAsync()).Set<IdentityUserRole>().Where(x => x.RoleId == roleId)
.Select(x => x.UserId).ToListAsync(GetCancellationToken(cancellationToken));
}
public virtual async Task<List<IdentityUser>> GetListAsync( public virtual async Task<List<IdentityUser>> GetListAsync(
string sorting = null, string sorting = null,
int maxResultCount = int.MaxValue, int maxResultCount = int.MaxValue,

10
modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs

@ -192,6 +192,16 @@ public class EfCoreOrganizationUnitRepository
.ToListAsync(GetCancellationToken(cancellationToken)); .ToListAsync(GetCancellationToken(cancellationToken));
} }
public virtual async Task<List<Guid>> GetMemberIdsAsync(Guid id, CancellationToken cancellationToken = default)
{
var dbContext = await GetDbContextAsync();
return await (from userOu in dbContext.Set<IdentityUserOrganizationUnit>()
join user in dbContext.Users on userOu.UserId equals user.Id
where userOu.OrganizationUnitId == id
select user.Id).ToListAsync(cancellationToken);
}
public virtual async Task<int> GetMembersCountAsync( public virtual async Task<int> GetMembersCountAsync(
OrganizationUnit organizationUnit, OrganizationUnit organizationUnit,
string filter = null, string filter = null,

28
modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs

@ -131,7 +131,17 @@ public class MongoIdentityUserRepository : MongoDbRepository<IAbpIdentityMongoDb
.Where(u => u.Roles.Any(r => r.RoleId == role.Id)) .Where(u => u.Roles.Any(r => r.RoleId == role.Id))
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
} }
public virtual async Task<List<Guid>> 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<List<IdentityUser>> GetListAsync( public virtual async Task<List<IdentityUser>> GetListAsync(
string sorting = null, string sorting = null,
int maxResultCount = int.MaxValue, int maxResultCount = int.MaxValue,
@ -270,7 +280,7 @@ public class MongoIdentityUserRepository : MongoDbRepository<IAbpIdentityMongoDb
.WhereIf<IdentityUser, IMongoQueryable<IdentityUser>>(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime) .WhereIf<IdentityUser, IMongoQueryable<IdentityUser>>(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime)
.LongCountAsync(GetCancellationToken(cancellationToken)); .LongCountAsync(GetCancellationToken(cancellationToken));
} }
public virtual async Task<List<IdentityUser>> GetUsersInOrganizationUnitAsync( public virtual async Task<List<IdentityUser>> GetUsersInOrganizationUnitAsync(
Guid organizationUnitId, Guid organizationUnitId,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
@ -301,7 +311,7 @@ public class MongoIdentityUserRepository : MongoDbRepository<IAbpIdentityMongoDb
.Where(ou => ou.Code.StartsWith(code)) .Where(ou => ou.Code.StartsWith(code))
.Select(ou => ou.Id) .Select(ou => ou.Id)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
return await (await GetMongoQueryableAsync(cancellationToken)) return await (await GetMongoQueryableAsync(cancellationToken))
.Where(u => u.OrganizationUnits.Any(uou => organizationUnitIds.Contains(uou.OrganizationUnitId))) .Where(u => u.OrganizationUnits.Any(uou => organizationUnitIds.Contains(uou.OrganizationUnitId)))
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
@ -364,29 +374,29 @@ public class MongoIdentityUserRepository : MongoDbRepository<IAbpIdentityMongoDb
} }
public virtual async Task<List<IdentityUserIdWithRoleNames>> GetRoleNamesAsync( public virtual async Task<List<IdentityUserIdWithRoleNames>> GetRoleNamesAsync(
IEnumerable<Guid> userIds, IEnumerable<Guid> userIds,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
cancellationToken = GetCancellationToken(cancellationToken); cancellationToken = GetCancellationToken(cancellationToken);
var userAndRoleIds = (await GetMongoQueryableAsync<IdentityUser>(cancellationToken)) var userAndRoleIds = (await GetMongoQueryableAsync<IdentityUser>(cancellationToken))
.Where(u => userIds.Contains(u.Id)) .Where(u => userIds.Contains(u.Id))
.SelectMany(u => u.Roles) .SelectMany(u => u.Roles)
.Select(userRole => new .Select(userRole => new
{ {
userRole.UserId, userRole.UserId,
userRole.RoleId userRole.RoleId
}).GroupBy(x => x.UserId).ToDictionary(x => x.Key, x => x.Select(r => r.RoleId).ToList()); }).GroupBy(x => x.UserId).ToDictionary(x => x.Key, x => x.Select(r => r.RoleId).ToList());
var roleIds = userAndRoleIds.SelectMany(x => x.Value); var roleIds = userAndRoleIds.SelectMany(x => x.Value);
var roles = await (await GetMongoQueryableAsync<IdentityRole>(cancellationToken)).Where(r => roleIds.Contains(r.Id)).Select(r => new var roles = await (await GetMongoQueryableAsync<IdentityRole>(cancellationToken)).Where(r => roleIds.Contains(r.Id)).Select(r => new
{ {
r.Id, r.Id,
r.Name r.Name
}).ToListAsync(cancellationToken); }).ToListAsync(cancellationToken);
var result = userAndRoleIds.ToDictionary(x => x.Key, x => roles.Where(r => x.Value.Contains(r.Id)).Select(r => r.Name).ToArray()); 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(); return result.Select(x => new IdentityUserIdWithRoleNames() { Id = x.Key, RoleNames = x.Value }).ToList();
} }
} }

8
modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoOrganizationUnitRepository.cs

@ -181,6 +181,14 @@ public class MongoOrganizationUnitRepository
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
} }
public virtual async Task<List<Guid>> GetMemberIdsAsync(Guid id, CancellationToken cancellationToken = default)
{
cancellationToken = GetCancellationToken(cancellationToken);
return await (await GetMongoQueryableAsync<IdentityUser>(cancellationToken))
.Where(u => u.OrganizationUnits.Any(uou => uou.OrganizationUnitId == id)).Select(x => x.Id)
.ToListAsync(cancellationToken);
}
public virtual async Task<int> GetMembersCountAsync( public virtual async Task<int> GetMembersCountAsync(
OrganizationUnit organizationUnit, OrganizationUnit organizationUnit,
string filter = null, string filter = null,

155
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.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using Shouldly; using Shouldly;
@ -9,6 +10,10 @@ namespace Volo.Abp.Identity;
public class IdentityDynamicClaimsPrincipalContributor_Tests : AbpIdentityDomainTestBase public class IdentityDynamicClaimsPrincipalContributor_Tests : AbpIdentityDomainTestBase
{ {
private readonly IdentityUserManager _identityUserManager; 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 IAbpClaimsPrincipalFactory _abpClaimsPrincipalFactory;
private readonly AbpUserClaimsPrincipalFactory _abpUserClaimsPrincipalFactory; private readonly AbpUserClaimsPrincipalFactory _abpUserClaimsPrincipalFactory;
private readonly IdentityTestData _testData; private readonly IdentityTestData _testData;
@ -16,13 +21,17 @@ public class IdentityDynamicClaimsPrincipalContributor_Tests : AbpIdentityDomain
public IdentityDynamicClaimsPrincipalContributor_Tests() public IdentityDynamicClaimsPrincipalContributor_Tests()
{ {
_identityUserManager = GetRequiredService<IdentityUserManager>(); _identityUserManager = GetRequiredService<IdentityUserManager>();
_identityRoleRepository = GetRequiredService<IIdentityRoleRepository>();
_identityRoleManager = GetRequiredService<IdentityRoleManager>();
_organizationUnitRepository = GetRequiredService<IOrganizationUnitRepository>();
_organizationUnitManager = GetRequiredService<OrganizationUnitManager>();
_abpClaimsPrincipalFactory = GetRequiredService<IAbpClaimsPrincipalFactory>(); _abpClaimsPrincipalFactory = GetRequiredService<IAbpClaimsPrincipalFactory>();
_abpUserClaimsPrincipalFactory = GetRequiredService<AbpUserClaimsPrincipalFactory>(); _abpUserClaimsPrincipalFactory = GetRequiredService<AbpUserClaimsPrincipalFactory>();
_testData = GetRequiredService<IdentityTestData>(); _testData = GetRequiredService<IdentityTestData>();
} }
[Fact] [Fact]
public async Task Should_Get_Correct_Claims_After_User_Updating() public async Task Should_Get_Correct_Claims_After_User_Updated()
{ {
IdentityUser user = null; IdentityUser user = null;
ClaimsPrincipal claimsPrincipal = 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.Name && x.Value == user.UserName);
claimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Email && x.Value == user.Email); 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 == "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); 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.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.Name && x.Value == user.UserName);
dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Email && x.Value == user.Email); 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 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.SetUserNameAsync(user, "newUserName");
await _identityUserManager.SetEmailAsync(user, "newUserEmail@abp.io"); 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.Name && x.Value =="newUserName");
dynamicClaimsPrincipal.Claims.ShouldContain(x => x.Type == ClaimTypes.Email && x.Value == "newUserEmail@abp.io"); 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 == "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.
} }
} }

5
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"); _supporterRole = new IdentityRole(_testData.RoleSupporterId, "supporter");
await _roleRepository.InsertAsync(_supporterRole); await _roleRepository.InsertAsync(_supporterRole);
_managerRole = new IdentityRole(_guidGenerator.Create(), "manager"); _managerRole = new IdentityRole(_testData.RoleManagerId, "manager");
await _roleRepository.InsertAsync(_managerRole); await _roleRepository.InsertAsync(_managerRole);
var saleRole = new IdentityRole(_testData.RoleSaleId, "sale");
await _roleRepository.InsertAsync(saleRole);
} }
/* Creates OU tree as shown below: /* Creates OU tree as shown below:

5
modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityRoleRepository_Tests.cs

@ -66,16 +66,17 @@ public abstract class IdentityRoleRepository_Tests<TStartupModule> : AbpIdentity
role.Claims.ShouldNotBeNull(); role.Claims.ShouldNotBeNull();
role.Claims.Any().ShouldBeTrue(); role.Claims.Any().ShouldBeTrue();
} }
[Fact] [Fact]
public async Task GetListWithUserCountAsync() public async Task GetListWithUserCountAsync()
{ {
var roles = await RoleRepository.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 == "admin" && r.UserCount == 2);
roles.ShouldContain(r => r.Role.Name == "moderator" && r.UserCount == 1); 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 == "supporter" && r.UserCount == 2);
roles.ShouldContain(r => r.Role.Name == "manager" && r.UserCount == 1); roles.ShouldContain(r => r.Role.Name == "manager" && r.UserCount == 1);
roles.ShouldContain(r => r.Role.Name == "sale" && r.UserCount == 0);
} }
} }

4
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 class IdentityTestData : ISingletonDependency
{ {
public Guid RoleModeratorId { get; } = Guid.NewGuid(); public Guid RoleModeratorId { get; } = Guid.NewGuid();
public Guid RoleSupporterId { 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 UserJohnId { get; } = Guid.NewGuid();
public Guid UserDavidId { get; } = Guid.NewGuid(); public Guid UserDavidId { get; } = Guid.NewGuid();
public Guid UserNeoId { get; } = Guid.NewGuid(); public Guid UserNeoId { get; } = Guid.NewGuid();

27
modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs

@ -14,6 +14,7 @@ public abstract class IdentityUserRepository_Tests<TStartupModule> : AbpIdentity
where TStartupModule : IAbpModule where TStartupModule : IAbpModule
{ {
protected IIdentityUserRepository UserRepository { get; } protected IIdentityUserRepository UserRepository { get; }
protected IIdentityRoleRepository RoleRepository { get; }
protected ILookupNormalizer LookupNormalizer { get; } protected ILookupNormalizer LookupNormalizer { get; }
protected IOrganizationUnitRepository OrganizationUnitRepository { get; } protected IOrganizationUnitRepository OrganizationUnitRepository { get; }
protected OrganizationUnitManager OrganizationUnitManager { get; } protected OrganizationUnitManager OrganizationUnitManager { get; }
@ -22,6 +23,7 @@ public abstract class IdentityUserRepository_Tests<TStartupModule> : AbpIdentity
protected IdentityUserRepository_Tests() protected IdentityUserRepository_Tests()
{ {
UserRepository = GetRequiredService<IIdentityUserRepository>(); UserRepository = GetRequiredService<IIdentityUserRepository>();
RoleRepository = GetRequiredService<IIdentityRoleRepository>();
LookupNormalizer = GetRequiredService<ILookupNormalizer>(); LookupNormalizer = GetRequiredService<ILookupNormalizer>();
OrganizationUnitRepository = GetRequiredService<IOrganizationUnitRepository>(); OrganizationUnitRepository = GetRequiredService<IOrganizationUnitRepository>();
OrganizationUnitManager = GetRequiredService<OrganizationUnitManager>();; OrganizationUnitManager = GetRequiredService<OrganizationUnitManager>();;
@ -53,7 +55,7 @@ public abstract class IdentityUserRepository_Tests<TStartupModule> : AbpIdentity
roles.ShouldContain("supporter"); roles.ShouldContain("supporter");
roles.ShouldContain("manager"); roles.ShouldContain("manager");
} }
[Fact] [Fact]
public async Task GetRoleNames_By_UserIds_Async() public async Task GetRoleNames_By_UserIds_Async()
{ {
@ -63,18 +65,18 @@ public abstract class IdentityUserRepository_Tests<TStartupModule> : AbpIdentity
TestData.UserNeoId, TestData.UserNeoId,
TestData.UserDavidId TestData.UserDavidId
}); });
userRoleNames.Count.ShouldBe(3); userRoleNames.Count.ShouldBe(3);
var userBob = userRoleNames.First(x => x.Id == TestData.UserBobId); var userBob = userRoleNames.First(x => x.Id == TestData.UserBobId);
userBob.RoleNames.Length.ShouldBe(1); userBob.RoleNames.Length.ShouldBe(1);
userBob.RoleNames[0].ShouldBe("manager"); userBob.RoleNames[0].ShouldBe("manager");
var userJohn = userRoleNames.First(x => x.Id == TestData.UserJohnId); var userJohn = userRoleNames.First(x => x.Id == TestData.UserJohnId);
userJohn.RoleNames.Length.ShouldBe(2); userJohn.RoleNames.Length.ShouldBe(2);
userJohn.RoleNames.ShouldContain("moderator"); userJohn.RoleNames.ShouldContain("moderator");
userJohn.RoleNames.ShouldContain("supporter"); userJohn.RoleNames.ShouldContain("supporter");
var userNeo = userRoleNames.First(x => x.Id == TestData.UserNeoId); var userNeo = userRoleNames.First(x => x.Id == TestData.UserNeoId);
userNeo.RoleNames.Length.ShouldBe(1); userNeo.RoleNames.Length.ShouldBe(1);
userNeo.RoleNames[0].ShouldBe("supporter"); userNeo.RoleNames[0].ShouldBe("supporter");
@ -121,6 +123,21 @@ public abstract class IdentityUserRepository_Tests<TStartupModule> : AbpIdentity
users.ShouldContain(u => u.UserName == "neo"); 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] [Fact]
public async Task GetListAsync() public async Task GetListAsync()
{ {

11
modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs

@ -232,6 +232,17 @@ public abstract class OrganizationUnitRepository_Tests<TStartupModule> : AbpIden
users.Count.ShouldBe(0); 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] [Fact]
public async Task GetMembersCountOfOrganizationUnit() public async Task GetMembersCountOfOrganizationUnit()
{ {

Loading…
Cancel
Save