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 d5f51ae71b..a9eefe019e 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 @@ public interface IIdentityRoleRepository : IBasicRepository bool includeDetails = false, CancellationToken cancellationToken = default ); - + Task> GetListAsync( string sorting = null, int maxResultCount = int.MaxValue, @@ -45,4 +45,10 @@ public interface IIdentityRoleRepository : IBasicRepository 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/IIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs index b8d5679aff..f17f74b602 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 @@ public interface IIdentityUserRepository : IBasicRepository CancellationToken cancellationToken = default ); + Task RemoveClaimFromAllUsersAsync( + 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 d7a13c8476..bc8ab16d9f 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,17 @@ namespace Volo.Abp.Identity; public class IdentityClaimTypeManager : DomainService { protected IIdentityClaimTypeRepository IdentityClaimTypeRepository { get; } + protected IIdentityUserRepository IdentityUserRepository { get; } + protected IIdentityRoleRepository IdentityRoleRepository { get; } - public IdentityClaimTypeManager(IIdentityClaimTypeRepository identityClaimTypeRepository) + public IdentityClaimTypeManager( + IIdentityClaimTypeRepository identityClaimTypeRepository, + IIdentityUserRepository identityUserRepository, + IIdentityRoleRepository identityRoleRepository) { IdentityClaimTypeRepository = identityClaimTypeRepository; + IdentityUserRepository = identityUserRepository; + IdentityRoleRepository = identityRoleRepository; } public virtual async Task CreateAsync(IdentityClaimType claimType) @@ -34,7 +43,21 @@ public class IdentityClaimTypeManager : DomainService throw new AbpException($"Can not update a static ClaimType."); } - 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 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 57843adad0..293b786eca 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 class EfCoreIdentityRoleRepository : EfCoreRepository> 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 class EfCoreIdentityRoleRepository : EfCoreRepository().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.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs index 2b261b065e..a05e59a1d0 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 @@ public class EfCoreIdentityUserRepository : EfCoreRepository 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 @@ public class EfCoreIdentityUserRepository : EfCoreRepository 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,20 @@ public class EfCoreIdentityUserRepository : EfCoreRepository().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 +230,7 @@ public class EfCoreIdentityUserRepository : EfCoreRepository().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/MongoIdentityRoleRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityRoleRepository.cs index e94bacb92c..ca67864975 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 class MongoIdentityRoleRepository : MongoDbRepository 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 class MongoIdentityRoleRepository : MongoDbRepository 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/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 1ce97c4dfd..2c99f872ba 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 class MongoIdentityUserRepository : MongoDbRepository 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 class MongoIdentityUserRepository : MongoDbRepository>() @@ -365,17 +379,17 @@ public class MongoIdentityUserRepository : MongoDbRepository 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 class MongoIdentityUserRepository : MongoDbRepository 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 @@ public class MongoIdentityUserRepository : MongoDbRepository(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 9a86f4964b..d8f79b91bf 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; @@ -15,12 +17,26 @@ 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(); } [Fact] @@ -42,7 +58,7 @@ public abstract class IdentityClaimTypeRepository_Tests : AbpIde { (await ClaimTypeRepository.GetCountAsync("Age")).ShouldBe(1); } - + [Fact] public async Task GetListAsyncByNames() { @@ -50,4 +66,45 @@ public abstract class IdentityClaimTypeRepository_Tests : AbpIde 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"); + + 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 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(); + } + } }