From 54e52eaafd1dba6e55d3e5e8942f8a9b6d662e85 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 17 May 2024 11:22:45 +0800 Subject: [PATCH 1/4] Add `RemoveClaimFromAllUsers` to `IIdentityUserRepository`. --- .../Abp/Identity/IIdentityUserRepository.cs | 6 +++ .../Abp/Identity/IdentityClaimTypeManager.cs | 21 ++++++++- .../EfCoreIdentityUserRepository.cs | 25 ++++++++-- .../MongoDB/MongoIdentityUserRepository.cs | 34 ++++++++++---- .../IdentityClaimTypeRepository_Tests.cs | 46 ++++++++++++++++++- 5 files changed, 114 insertions(+), 18 deletions(-) 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 b8d5679aff9..d23954e11d8 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 @@ -44,6 +44,12 @@ Task> GetListByClaimAsync( CancellationToken cancellationToken = default ); + Task RemoveClaimFromAllUsers( + string claimType, + bool autoSave = false, + CancellationToken cancellationToken = default + ); + Task> GetListByNormalizedRoleNameAsync( string normalizedRoleName, bool includeDetails = false, diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityClaimTypeManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityClaimTypeManager.cs index d7a13c8476f..19b18ec56d5 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityClaimTypeManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityClaimTypeManager.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; using Volo.Abp.Domain.Services; namespace Volo.Abp.Identity; @@ -6,10 +8,12 @@ namespace Volo.Abp.Identity; public class IdentityClaimTypeManager : DomainService { protected IIdentityClaimTypeRepository IdentityClaimTypeRepository { get; } + protected IIdentityUserRepository IdentityUserRepository { get; } - public IdentityClaimTypeManager(IIdentityClaimTypeRepository identityClaimTypeRepository) + public IdentityClaimTypeManager(IIdentityClaimTypeRepository identityClaimTypeRepository, IIdentityUserRepository identityUserRepository) { IdentityClaimTypeRepository = identityClaimTypeRepository; + IdentityUserRepository = identityUserRepository; } public virtual async Task CreateAsync(IdentityClaimType claimType) @@ -37,4 +41,17 @@ public virtual async Task UpdateAsync(IdentityClaimType claim return await IdentityClaimTypeRepository.UpdateAsync(claimType); } + + public virtual async Task DeleteAsync(Guid id) + { + var claimType = await IdentityClaimTypeRepository.GetAsync(id); + if (claimType.IsStatic) + { + throw new AbpException($"Can not delete a static ClaimType."); + } + + //Remove claim of this type from all users + await IdentityUserRepository.RemoveClaimFromAllUsers(claimType.Name); + await IdentityClaimTypeRepository.DeleteAsync(id); + } } 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 2b261b065e0..ca6988d72b1 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 @@ -74,7 +74,7 @@ into gp { Id = gp.Key, RoleNames = gp.Select(x => x.Name).ToArray() }).ToListAsync(cancellationToken: cancellationToken); - + var orgUnitRoles = await (from userOu in dbContext.Set() join roleOu in dbContext.Set() on userOu.OrganizationUnitId equals roleOu.OrganizationUnitId join role in dbContext.Roles on roleOu.RoleId equals role.Id @@ -89,7 +89,7 @@ into gp { Id = gp.Key, RoleNames = gp.Select(x => x.Name).ToArray() }).ToListAsync(cancellationToken: cancellationToken); - + return userRoles.Concat(orgUnitRoles).GroupBy(x => x.Id).Select(x => new IdentityUserIdWithRoleNames {Id = x.Key, RoleNames = x.SelectMany(y => y.RoleNames).Distinct().ToArray()}).ToList(); } @@ -145,6 +145,21 @@ public virtual async Task> GetListByClaimAsync( .ToListAsync(GetCancellationToken(cancellationToken)); } + public virtual async Task RemoveClaimFromAllUsers(string claimType, bool autoSave, CancellationToken cancellationToken = default) + { + var dbContext = await GetDbContextAsync(); + var userClaims = await dbContext.Set().Where(uc => uc.ClaimType == claimType).ToListAsync(cancellationToken: cancellationToken); + if (userClaims.Any()) + { + (await GetDbContextAsync()).Set().RemoveRange(userClaims); + } + + if (autoSave) + { + await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken)); + } + } + public virtual async Task> GetListByNormalizedRoleNameAsync( string normalizedRoleName, bool includeDetails = false, @@ -216,7 +231,7 @@ public virtual async Task> GetListAsync( minModifitionTime, cancellationToken ); - + return await query.IncludeDetails(includeDetails) .OrderBy(sorting.IsNullOrWhiteSpace() ? nameof(IdentityUser.UserName) : sorting) .PageBy(skipCount, maxResultCount) @@ -437,14 +452,14 @@ protected virtual async Task> GetFilteredQueryableAsync { var upperFilter = filter?.ToUpperInvariant(); var query = await GetQueryableAsync(); - + if (roleId.HasValue) { var dbContext = await GetDbContextAsync(); var organizationUnitIds = await dbContext.Set().Where(q => q.RoleId == roleId.Value).Select(q => q.OrganizationUnitId).ToArrayAsync(cancellationToken: cancellationToken); query = query.Where(identityUser => identityUser.Roles.Any(x => x.RoleId == roleId.Value) || identityUser.OrganizationUnits.Any(x => organizationUnitIds.Contains(x.OrganizationUnitId))); } - + return query .WhereIf( !filter.IsNullOrWhiteSpace(), 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 1ce97c4dfd1..2f4560fef97 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 @@ -108,6 +108,20 @@ public virtual async Task> GetListByClaimAsync( .ToListAsync(GetCancellationToken(cancellationToken)); } + public virtual async Task RemoveClaimFromAllUsers(string claimType, bool autoSave, CancellationToken cancellationToken = default) + { + var users = await (await GetMongoQueryableAsync(cancellationToken)) + .Where(u => u.Claims.Any(c => c.ClaimType == claimType)) + .ToListAsync(GetCancellationToken(cancellationToken)); + + foreach (var user in users) + { + user.Claims.RemoveAll(c => c.ClaimType == claimType); + } + + await UpdateManyAsync(users, cancellationToken: cancellationToken); + } + public virtual async Task> GetListByNormalizedRoleNameAsync( string normalizedRoleName, bool includeDetails = false, @@ -164,7 +178,7 @@ public virtual async Task> GetListAsync( DateTime? maxModifitionTime = null, DateTime? minModifitionTime = null, CancellationToken cancellationToken = default) - { + { var query = await GetFilteredQueryableAsync( filter, roleId, @@ -184,7 +198,7 @@ public virtual async Task> GetListAsync( minModifitionTime, cancellationToken ); - + return await query .OrderBy(sorting.IsNullOrWhiteSpace() ? nameof(IdentityUser.UserName) : sorting) .As>() @@ -365,17 +379,17 @@ public virtual async Task> GetRoleNamesAsync( CancellationToken cancellationToken = default) { var users = await GetListByIdsAsync(userIds, cancellationToken: cancellationToken); - + var userAndRoleIds = users.SelectMany(u => u.Roles) .Select(userRole => new { userRole.UserId, userRole.RoleId }) .GroupBy(x => x.UserId).ToDictionary(x => x.Key, x => x.Select(r => r.RoleId).ToList()); var userAndOrganizationUnitIds = users.SelectMany(u => u.OrganizationUnits) .Select(userOrganizationUnit => new { userOrganizationUnit.UserId, userOrganizationUnit.OrganizationUnitId }) .GroupBy(x => x.UserId).ToDictionary(x => x.Key, x => x.Select(r => r.OrganizationUnitId).ToList()); - + var organizationUnitIds = userAndOrganizationUnitIds.SelectMany(x => x.Value); var roleIds = userAndRoleIds.SelectMany(x => x.Value); - + var organizationUnitAndRoleIds = await (await GetMongoQueryableAsync(cancellationToken)).Where(ou => organizationUnitIds.Contains(ou.Id)) .Select(userOrganizationUnit => new { @@ -384,10 +398,10 @@ public virtual async Task> GetRoleNamesAsync( }).ToListAsync(cancellationToken: cancellationToken); var allOrganizationUnitRoleIds = organizationUnitAndRoleIds.SelectMany(x => x.Roles.Select(r => r.RoleId)).ToList(); var allRoleIds = roleIds.Union(allOrganizationUnitRoleIds); - + var roles = await (await GetMongoQueryableAsync(cancellationToken)).Where(r => allRoleIds.Contains(r.Id)).Select(r => new{ r.Id, r.Name }).ToListAsync(cancellationToken); var userRoles = userAndRoleIds.ToDictionary(x => x.Key, x => roles.Where(r => x.Value.Contains(r.Id)).Select(r => r.Name).ToArray()); - + var result = userRoles.Select(x => new IdentityUserIdWithRoleNames { Id = x.Key, RoleNames = x.Value }).ToList(); foreach (var userAndOrganizationUnitId in userAndOrganizationUnitIds) @@ -429,17 +443,17 @@ protected virtual async Task> GetFilteredQueryable { var upperFilter = filter?.ToUpperInvariant(); var query = await GetMongoQueryableAsync(cancellationToken); - + if (roleId.HasValue) { var organizationUnitIds = (await GetMongoQueryableAsync(cancellationToken)) .Where(ou => ou.Roles.Any(r => r.RoleId == roleId.Value)) .Select(userOrganizationUnit => userOrganizationUnit.Id) .ToArray(); - + query = query.Where(identityUser => identityUser.Roles.Any(x => x.RoleId == roleId.Value) || identityUser.OrganizationUnits.Any(x => organizationUnitIds.Contains(x.OrganizationUnitId))); } - + return query .WhereIf>( !filter.IsNullOrWhiteSpace(), diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityClaimTypeRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityClaimTypeRepository_Tests.cs index 9a86f4964b5..6028a075202 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityClaimTypeRepository_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityClaimTypeRepository_Tests.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Claims; using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp.Guids; using Volo.Abp.Modularity; +using Volo.Abp.Uow; using Xunit; namespace Volo.Abp.Identity; @@ -16,11 +18,19 @@ public abstract class IdentityClaimTypeRepository_Tests : AbpIde { protected IIdentityClaimTypeRepository ClaimTypeRepository { get; } protected IGuidGenerator GuidGenerator { get; } + protected IUnitOfWorkManager UnitOfWorkManager { get; } + protected IIdentityUserRepository UserRepository { get; } + protected IdentityUserManager IdentityUserManager { get; } + protected IdentityTestData IdentityTestData { get; } public IdentityClaimTypeRepository_Tests() { ClaimTypeRepository = ServiceProvider.GetRequiredService(); GuidGenerator = ServiceProvider.GetRequiredService(); + UnitOfWorkManager = ServiceProvider.GetRequiredService(); + IdentityUserManager = ServiceProvider.GetRequiredService(); + UserRepository = ServiceProvider.GetRequiredService(); + IdentityTestData = ServiceProvider.GetRequiredService(); } [Fact] @@ -42,7 +52,7 @@ public async Task GetCountAsync_With_Filter() { (await ClaimTypeRepository.GetCountAsync("Age")).ShouldBe(1); } - + [Fact] public async Task GetListAsyncByNames() { @@ -50,4 +60,38 @@ public async Task GetListAsyncByNames() result.Count.ShouldBe(2); } + + [Fact] + public async Task DeleteAsync() + { + var ageClaim = await ClaimTypeRepository.FindAsync(IdentityTestData.AgeClaimId); + ageClaim.ShouldNotBeNull(); + + using (var uow = UnitOfWorkManager.Begin()) + { + var john = await UserRepository.FindAsync(IdentityTestData.UserJohnId); + john.ShouldNotBeNull(); + await IdentityUserManager.AddClaimAsync(john, new Claim(ageClaim.Name, "18")); + + var userClaims = await IdentityUserManager.GetClaimsAsync(john); + userClaims.ShouldContain(c => c.Type == ageClaim.Name && c.Value == "18"); + + await uow.CompleteAsync(); + } + + await ClaimTypeRepository.DeleteAsync(ageClaim.Id); + await UserRepository.RemoveClaimFromAllUsers(ageClaim.Name); + + using (var uow = UnitOfWorkManager.Begin()) + { + var john = await UserRepository.FindAsync(IdentityTestData.UserJohnId); + john.ShouldNotBeNull(); + + var userClaims = await IdentityUserManager.GetClaimsAsync(john); + + userClaims.ShouldNotContain(c => c.Type == ageClaim.Name && c.Value == "18"); + + await uow.CompleteAsync(); + } + } } From 3a7b4191f2eb8b946a4891acb7f7a7eb205c9b47 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 17 May 2024 11:33:29 +0800 Subject: [PATCH 2/4] Rename to `RemoveClaimFromAllUsersAsync `. --- .../Volo/Abp/Identity/IIdentityUserRepository.cs | 2 +- .../Volo/Abp/Identity/IdentityClaimTypeManager.cs | 2 +- .../EntityFrameworkCore/EfCoreIdentityUserRepository.cs | 2 +- .../Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs | 2 +- .../Volo/Abp/Identity/IdentityClaimTypeRepository_Tests.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) 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 d23954e11d8..f17f74b6029 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 @@ -44,7 +44,7 @@ Task> GetListByClaimAsync( CancellationToken cancellationToken = default ); - Task RemoveClaimFromAllUsers( + Task RemoveClaimFromAllUsersAsync( string claimType, bool autoSave = false, CancellationToken cancellationToken = default diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityClaimTypeManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityClaimTypeManager.cs index 19b18ec56d5..0ccca2d458e 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityClaimTypeManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityClaimTypeManager.cs @@ -51,7 +51,7 @@ public virtual async Task DeleteAsync(Guid id) } //Remove claim of this type from all users - await IdentityUserRepository.RemoveClaimFromAllUsers(claimType.Name); + await IdentityUserRepository.RemoveClaimFromAllUsersAsync(claimType.Name); await IdentityClaimTypeRepository.DeleteAsync(id); } } 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 ca6988d72b1..561a2249263 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 @@ -145,7 +145,7 @@ public virtual async Task> GetListByClaimAsync( .ToListAsync(GetCancellationToken(cancellationToken)); } - public virtual async Task RemoveClaimFromAllUsers(string claimType, bool autoSave, CancellationToken cancellationToken = default) + public virtual async Task RemoveClaimFromAllUsersAsync(string claimType, bool autoSave, CancellationToken cancellationToken = default) { var dbContext = await GetDbContextAsync(); var userClaims = await dbContext.Set().Where(uc => uc.ClaimType == claimType).ToListAsync(cancellationToken: cancellationToken); 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 2f4560fef97..2c99f872ba5 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 @@ -108,7 +108,7 @@ public virtual async Task> GetListByClaimAsync( .ToListAsync(GetCancellationToken(cancellationToken)); } - public virtual async Task RemoveClaimFromAllUsers(string claimType, bool autoSave, CancellationToken cancellationToken = default) + public virtual async Task RemoveClaimFromAllUsersAsync(string claimType, bool autoSave, CancellationToken cancellationToken = default) { var users = await (await GetMongoQueryableAsync(cancellationToken)) .Where(u => u.Claims.Any(c => c.ClaimType == claimType)) diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityClaimTypeRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityClaimTypeRepository_Tests.cs index 6028a075202..1fd7f4f227d 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityClaimTypeRepository_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityClaimTypeRepository_Tests.cs @@ -80,7 +80,7 @@ public async Task DeleteAsync() } await ClaimTypeRepository.DeleteAsync(ageClaim.Id); - await UserRepository.RemoveClaimFromAllUsers(ageClaim.Name); + await UserRepository.RemoveClaimFromAllUsersAsync(ageClaim.Name); using (var uow = UnitOfWorkManager.Begin()) { From 8edeb698ce87cb41d40827f473bfb07e26b3d199 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 17 May 2024 13:00:36 +0800 Subject: [PATCH 3/4] Update EfCoreIdentityUserRepository.cs --- .../EntityFrameworkCore/EfCoreIdentityUserRepository.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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 561a2249263..a05e59a1d04 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 @@ -152,11 +152,10 @@ public virtual async Task RemoveClaimFromAllUsersAsync(string claimType, bool au if (userClaims.Any()) { (await GetDbContextAsync()).Set().RemoveRange(userClaims); - } - - if (autoSave) - { - await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken)); + if (autoSave) + { + await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken)); + } } } From f253c336a758fb76bd0645d7e248ddb1340123a3 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 17 May 2024 20:21:47 +0800 Subject: [PATCH 4/4] Add `RemoveClaimFromAllRolesAsync` method to `IIdentityRoleRepository`. --- .../Abp/Identity/IIdentityRoleRepository.cs | 8 ++++++- .../Abp/Identity/IdentityClaimTypeManager.cs | 12 +++++++--- .../EfCoreIdentityRoleRepository.cs | 24 +++++++++++++++---- .../MongoDB/MongoIdentityRoleRepository.cs | 18 ++++++++++++-- .../IdentityClaimTypeRepository_Tests.cs | 23 ++++++++++++++---- 5 files changed, 69 insertions(+), 16 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityRoleRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityRoleRepository.cs index d5f51ae71bd..a9eefe019e6 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityRoleRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityRoleRepository.cs @@ -22,7 +22,7 @@ Task> GetListWithUserCountAsync( bool includeDetails = false, CancellationToken cancellationToken = default ); - + Task> GetListAsync( string sorting = null, int maxResultCount = int.MaxValue, @@ -45,4 +45,10 @@ Task GetCountAsync( string filter = null, CancellationToken cancellationToken = default ); + + Task RemoveClaimFromAllRolesAsync( + string claimType, + bool autoSave = false, + CancellationToken cancellationToken = default + ); } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityClaimTypeManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityClaimTypeManager.cs index 0ccca2d458e..bc8ab16d9fa 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityClaimTypeManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityClaimTypeManager.cs @@ -9,11 +9,16 @@ public class IdentityClaimTypeManager : DomainService { protected IIdentityClaimTypeRepository IdentityClaimTypeRepository { get; } protected IIdentityUserRepository IdentityUserRepository { get; } + protected IIdentityRoleRepository IdentityRoleRepository { get; } - public IdentityClaimTypeManager(IIdentityClaimTypeRepository identityClaimTypeRepository, IIdentityUserRepository identityUserRepository) + public IdentityClaimTypeManager( + IIdentityClaimTypeRepository identityClaimTypeRepository, + IIdentityUserRepository identityUserRepository, + IIdentityRoleRepository identityRoleRepository) { IdentityClaimTypeRepository = identityClaimTypeRepository; IdentityUserRepository = identityUserRepository; + IdentityRoleRepository = identityRoleRepository; } public virtual async Task CreateAsync(IdentityClaimType claimType) @@ -38,7 +43,6 @@ public virtual async Task UpdateAsync(IdentityClaimType claim throw new AbpException($"Can not update a static ClaimType."); } - return await IdentityClaimTypeRepository.UpdateAsync(claimType); } @@ -50,8 +54,10 @@ public virtual async Task DeleteAsync(Guid id) throw new AbpException($"Can not delete a static ClaimType."); } - //Remove claim of this type from all users + //Remove claim of this type from all users and roles await IdentityUserRepository.RemoveClaimFromAllUsersAsync(claimType.Name); + await IdentityRoleRepository.RemoveClaimFromAllRolesAsync(claimType.Name); + await IdentityClaimTypeRepository.DeleteAsync(id); } } diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityRoleRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityRoleRepository.cs index 57843adad01..293b786ecad 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityRoleRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityRoleRepository.cs @@ -29,26 +29,26 @@ public virtual async Task FindByNormalizedNameAsync( } public virtual async Task> GetListWithUserCountAsync( - string sorting = null, + string sorting = null, int maxResultCount = int.MaxValue, int skipCount = 0, string filter = null, - bool includeDetails = false, + bool includeDetails = false, CancellationToken cancellationToken = default) { var roles = await GetListInternalAsync(sorting, maxResultCount, skipCount, filter, includeDetails, cancellationToken: cancellationToken); - + var roleIds = roles.Select(x => x.Id).ToList(); var userCount = await (await GetDbContextAsync()).Set() .Where(userRole => roleIds.Contains(userRole.RoleId)) .GroupBy(userRole => userRole.RoleId) - .Select(x => new + .Select(x => new { RoleId = x.Key, Count = x.Count() }) .ToListAsync(GetCancellationToken(cancellationToken)); - + return roles.Select(role => new IdentityRoleWithUserCount(role, userCount.FirstOrDefault(x => x.RoleId == role.Id)?.Count ?? 0)).ToList(); } @@ -92,6 +92,20 @@ public virtual async Task GetCountAsync( .LongCountAsync(GetCancellationToken(cancellationToken)); } + public virtual async Task RemoveClaimFromAllRolesAsync(string claimType, bool autoSave = false, CancellationToken cancellationToken = default) + { + var dbContext = await GetDbContextAsync(); + var roleClaims = await dbContext.Set().Where(uc => uc.ClaimType == claimType).ToListAsync(cancellationToken: cancellationToken); + if (roleClaims.Any()) + { + (await GetDbContextAsync()).Set().RemoveRange(roleClaims); + if (autoSave) + { + await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken)); + } + } + } + protected virtual async Task> GetListInternalAsync( string sorting = null, int maxResultCount = int.MaxValue, diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityRoleRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityRoleRepository.cs index e94bacb92cc..ca678649752 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityRoleRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityRoleRepository.cs @@ -42,13 +42,13 @@ public virtual async Task> GetListWithUserCountA .Where(user => user.Roles.Any(role => roleIds.Contains(role.RoleId))) .SelectMany(user => user.Roles) .GroupBy(userRole => userRole.RoleId) - .Select(x => new + .Select(x => new { RoleId = x.Key, Count = x.Count() }) .ToListAsync(GetCancellationToken(cancellationToken)); - + return roles.Select(role => new IdentityRoleWithUserCount(role, userCount.FirstOrDefault(x => x.RoleId == role.Id)?.Count ?? 0)).ToList(); } @@ -99,6 +99,20 @@ public virtual async Task GetCountAsync( .LongCountAsync(GetCancellationToken(cancellationToken)); } + public virtual async Task RemoveClaimFromAllRolesAsync(string claimType, bool autoSave = false, CancellationToken cancellationToken = default) + { + var roles = await (await GetMongoQueryableAsync(cancellationToken)) + .Where(r => r.Claims.Any(c => c.ClaimType == claimType)) + .ToListAsync(GetCancellationToken(cancellationToken)); + + foreach (var role in roles) + { + role.Claims.RemoveAll(c => c.ClaimType == claimType); + } + + await UpdateManyAsync(roles, cancellationToken: cancellationToken); + } + protected virtual async Task> GetListInternalAsync( string sorting = null, int maxResultCount = int.MaxValue, diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityClaimTypeRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityClaimTypeRepository_Tests.cs index 1fd7f4f227d..d8f79b91bf0 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityClaimTypeRepository_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityClaimTypeRepository_Tests.cs @@ -17,19 +17,25 @@ public abstract class IdentityClaimTypeRepository_Tests : AbpIde where TStartupModule : IAbpModule { protected IIdentityClaimTypeRepository ClaimTypeRepository { get; } + protected IdentityClaimTypeManager IdentityClaimTypeManager { get; } protected IGuidGenerator GuidGenerator { get; } protected IUnitOfWorkManager UnitOfWorkManager { get; } protected IIdentityUserRepository UserRepository { get; } protected IdentityUserManager IdentityUserManager { get; } + protected IIdentityRoleRepository RoleRepository { get; } + protected IdentityRoleManager IdentityRoleManager { get; } protected IdentityTestData IdentityTestData { get; } public IdentityClaimTypeRepository_Tests() { ClaimTypeRepository = ServiceProvider.GetRequiredService(); + IdentityClaimTypeManager = ServiceProvider.GetRequiredService(); GuidGenerator = ServiceProvider.GetRequiredService(); UnitOfWorkManager = ServiceProvider.GetRequiredService(); IdentityUserManager = ServiceProvider.GetRequiredService(); UserRepository = ServiceProvider.GetRequiredService(); + RoleRepository = ServiceProvider.GetRequiredService(); + IdentityRoleManager = ServiceProvider.GetRequiredService(); IdentityTestData = ServiceProvider.GetRequiredService(); } @@ -72,25 +78,32 @@ public async Task DeleteAsync() var john = await UserRepository.FindAsync(IdentityTestData.UserJohnId); john.ShouldNotBeNull(); await IdentityUserManager.AddClaimAsync(john, new Claim(ageClaim.Name, "18")); - var userClaims = await IdentityUserManager.GetClaimsAsync(john); userClaims.ShouldContain(c => c.Type == ageClaim.Name && c.Value == "18"); + var saleRole = await RoleRepository.FindAsync(IdentityTestData.RoleSaleId); + saleRole.ShouldNotBeNull(); + await IdentityRoleManager.AddClaimAsync(saleRole, new Claim(ageClaim.Name, "18")); + var roleClaims = await IdentityRoleManager.GetClaimsAsync(saleRole); + roleClaims.ShouldContain(c => c.Type == ageClaim.Name && c.Value == "18"); + await uow.CompleteAsync(); } - await ClaimTypeRepository.DeleteAsync(ageClaim.Id); - await UserRepository.RemoveClaimFromAllUsersAsync(ageClaim.Name); + await IdentityClaimTypeManager.DeleteAsync(ageClaim.Id); using (var uow = UnitOfWorkManager.Begin()) { var john = await UserRepository.FindAsync(IdentityTestData.UserJohnId); john.ShouldNotBeNull(); - var userClaims = await IdentityUserManager.GetClaimsAsync(john); - userClaims.ShouldNotContain(c => c.Type == ageClaim.Name && c.Value == "18"); + var saleRole = await RoleRepository.FindAsync(IdentityTestData.RoleSaleId); + saleRole.ShouldNotBeNull(); + var roleClaims = await IdentityRoleManager.GetClaimsAsync(saleRole); + roleClaims.ShouldNotContain(c => c.Type == ageClaim.Name && c.Value == "18"); + await uow.CompleteAsync(); } }