diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityLinkUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityLinkUserRepository.cs index 2040924d10..9e86d4b34b 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityLinkUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityLinkUserRepository.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -14,6 +14,11 @@ namespace Volo.Abp.Identity CancellationToken cancellationToken = default); Task> GetListAsync( + IdentityLinkUserInfo linkUserInfo, + List excludes = null, + CancellationToken cancellationToken = default); + + Task DeleteAsync( IdentityLinkUserInfo linkUserInfo, CancellationToken cancellationToken = default); } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserLinkManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserLinkManager.cs index 6f8c600daa..967296bc28 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserLinkManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserLinkManager.cs @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Services; using Volo.Abp.MultiTenancy; @@ -20,6 +23,52 @@ namespace Volo.Abp.Identity CurrentTenant = currentTenant; } + public async Task> GetListAsync(IdentityLinkUserInfo linkUserInfo, + bool includeIndirect = false, CancellationToken cancellationToken = default) + { + var users = await IdentityLinkUserRepository.GetListAsync(linkUserInfo, cancellationToken: cancellationToken); + if (includeIndirect == false) + { + return users; + } + + var userInfos = new List() + { + linkUserInfo + }; + + var allUsers = new List(); + allUsers.AddRange(users); + + do + { + var nextUsers = new List(); + foreach (var user in users) + { + if (userInfos.Any(x => x.TenantId != user.SourceTenantId || x.UserId != user.SourceUserId)) + { + nextUsers.Add(new IdentityLinkUserInfo(user.SourceUserId, user.SourceTenantId)); + } + + if (userInfos.Any(x => x.TenantId != user.TargetTenantId || x.UserId != user.TargetUserId)) + { + nextUsers.Add(new IdentityLinkUserInfo(user.TargetUserId, user.TargetTenantId)); + } + } + + users = new List(); + foreach (var next in nextUsers) + { + users.AddRange(await IdentityLinkUserRepository.GetListAsync(next, userInfos, cancellationToken: cancellationToken)); + } + + userInfos.AddRange(nextUsers); + allUsers.AddRange(users); + } while (users.Any()); + + return allUsers; + } + public virtual async Task LinkAsync(IdentityLinkUserInfo sourceLinkUser, IdentityLinkUserInfo targetLinkUser) { if (sourceLinkUser.UserId == targetLinkUser.UserId && sourceLinkUser.TenantId == targetLinkUser.TenantId) diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityLinkUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityLinkUserRepository.cs index c7e81f327e..ab76f0cc48 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityLinkUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityLinkUserRepository.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -28,13 +28,35 @@ namespace Volo.Abp.Identity.EntityFrameworkCore , cancellationToken: GetCancellationToken(cancellationToken)); } - public virtual async Task> GetListAsync(IdentityLinkUserInfo linkUserInfo, CancellationToken cancellationToken = default) + public virtual async Task> GetListAsync(IdentityLinkUserInfo linkUserInfo, List excludes = null, + CancellationToken cancellationToken = default) { - return await (await GetDbSetAsync()) + IQueryable query = (await GetDbSetAsync()) .Where(x => + x.SourceUserId == linkUserInfo.UserId && x.SourceTenantId == linkUserInfo.TenantId || + x.TargetUserId == linkUserInfo.UserId && x.TargetTenantId == linkUserInfo.TenantId); + + if (!excludes.IsNullOrEmpty()) + { + foreach (var userInfo in excludes) + { + query = query.Where(x => + (x.SourceTenantId != userInfo.TenantId || x.SourceUserId != userInfo.UserId) && + (x.TargetTenantId != userInfo.TenantId || x.TargetUserId != userInfo.UserId)); + } + } + + return await query.ToListAsync(cancellationToken: GetCancellationToken(cancellationToken)); + } + + public virtual async Task DeleteAsync(IdentityLinkUserInfo linkUserInfo, CancellationToken cancellationToken = default) + { + var linkUsers = await (await GetDbSetAsync()).Where(x => x.SourceUserId == linkUserInfo.UserId && x.SourceTenantId == linkUserInfo.TenantId || x.TargetUserId == linkUserInfo.UserId && x.TargetTenantId == linkUserInfo.TenantId) .ToListAsync(cancellationToken: GetCancellationToken(cancellationToken)); + + await DeleteManyAsync(linkUsers, cancellationToken: cancellationToken); } } } diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityLinkUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityLinkUserRepository.cs index e670759e5c..bb30103dfc 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityLinkUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityLinkUserRepository.cs @@ -1,7 +1,6 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -using System.Linq.Dynamic.Core; using System.Threading; using System.Threading.Tasks; using MongoDB.Driver; @@ -28,12 +27,34 @@ namespace Volo.Abp.Identity.MongoDB , cancellationToken: GetCancellationToken(cancellationToken)); } - public virtual async Task> GetListAsync(IdentityLinkUserInfo linkUserInfo, CancellationToken cancellationToken = default) + public virtual async Task> GetListAsync(IdentityLinkUserInfo linkUserInfo, List excludes = null, + CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync(cancellationToken)).Where(x => + var query = (await GetMongoQueryableAsync(cancellationToken)).Where(x => + x.SourceUserId == linkUserInfo.UserId && x.SourceTenantId == linkUserInfo.TenantId || + x.TargetUserId == linkUserInfo.UserId && x.TargetTenantId == linkUserInfo.TenantId); + + if (!excludes.IsNullOrEmpty()) + { + foreach (var userInfo in excludes) + { + query = query.Where(x => + (x.SourceTenantId != userInfo.TenantId || x.SourceUserId != userInfo.UserId) && + (x.TargetTenantId != userInfo.TenantId || x.TargetUserId != userInfo.UserId)); + } + } + + return await query.ToListAsync(cancellationToken: GetCancellationToken(cancellationToken)); + } + + public virtual async Task DeleteAsync(IdentityLinkUserInfo linkUserInfo, CancellationToken cancellationToken = default) + { + var linkUsers = await (await GetMongoQueryableAsync(cancellationToken)).Where(x => x.SourceUserId == linkUserInfo.UserId && x.SourceTenantId == linkUserInfo.TenantId || x.TargetUserId == linkUserInfo.UserId && x.TargetTenantId == linkUserInfo.TenantId) .ToListAsync(cancellationToken: GetCancellationToken(cancellationToken)); + + await DeleteManyAsync(linkUsers, cancellationToken: cancellationToken); } } } diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityLinkUserManager_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityLinkUserManager_Tests.cs index 633f419da5..4bb77cad67 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityLinkUserManager_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityLinkUserManager_Tests.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Shouldly; using Xunit; @@ -19,6 +20,122 @@ namespace Volo.Abp.Identity TestData = GetRequiredService(); } + [Fact] + public async Task GetListAsync_Test() + { + var a = Guid.NewGuid(); + var b = Guid.NewGuid(); + var c = Guid.NewGuid(); + var d = Guid.NewGuid(); + var e = Guid.NewGuid(); + var f = Guid.NewGuid(); + var g = Guid.NewGuid(); + var h = Guid.NewGuid(); + var i = Guid.NewGuid(); + + await IdentityLinkUserRepository.InsertAsync(new IdentityLinkUser( + Guid.NewGuid(), + new IdentityLinkUserInfo(a, null), + new IdentityLinkUserInfo(b, null)), true); + + await IdentityLinkUserRepository.InsertAsync(new IdentityLinkUser( + Guid.NewGuid(), + new IdentityLinkUserInfo(c, null), + new IdentityLinkUserInfo(a, null)), true); + + await IdentityLinkUserRepository.InsertAsync(new IdentityLinkUser( + Guid.NewGuid(), + new IdentityLinkUserInfo(d, null), + new IdentityLinkUserInfo(c, null)), true); + + await IdentityLinkUserRepository.InsertAsync(new IdentityLinkUser( + Guid.NewGuid(), + new IdentityLinkUserInfo(e, null), + new IdentityLinkUserInfo(c, null)), true); + + await IdentityLinkUserRepository.InsertAsync(new IdentityLinkUser( + Guid.NewGuid(), + new IdentityLinkUserInfo(f, null), + new IdentityLinkUserInfo(e, null)), true); + + await IdentityLinkUserRepository.InsertAsync(new IdentityLinkUser( + Guid.NewGuid(), + new IdentityLinkUserInfo(g, null), + new IdentityLinkUserInfo(h, null)), true); + + await IdentityLinkUserRepository.InsertAsync(new IdentityLinkUser( + Guid.NewGuid(), + new IdentityLinkUserInfo(i, null), + new IdentityLinkUserInfo(h, null)), true); + + var linkUsers = await IdentityLinkUserManager.GetListAsync(new IdentityLinkUserInfo(a, null)); + linkUsers.Count.ShouldBe(2); + + linkUsers = await IdentityLinkUserManager.GetListAsync(new IdentityLinkUserInfo(f, null)); + linkUsers.Count.ShouldBe(1); + + linkUsers = await IdentityLinkUserManager.GetListAsync(new IdentityLinkUserInfo(g, null)); + linkUsers.Count.ShouldBe(1); + } + + [Fact] + public async Task GetListAsync_Indirect_Test() + { + var a = Guid.NewGuid(); + var b = Guid.NewGuid(); + var c = Guid.NewGuid(); + var d = Guid.NewGuid(); + var e = Guid.NewGuid(); + var f = Guid.NewGuid(); + var g = Guid.NewGuid(); + var h = Guid.NewGuid(); + var i = Guid.NewGuid(); + + await IdentityLinkUserRepository.InsertAsync(new IdentityLinkUser( + Guid.NewGuid(), + new IdentityLinkUserInfo(a, null), + new IdentityLinkUserInfo(b, null)), true); + + await IdentityLinkUserRepository.InsertAsync(new IdentityLinkUser( + Guid.NewGuid(), + new IdentityLinkUserInfo(c, null), + new IdentityLinkUserInfo(a, null)), true); + + await IdentityLinkUserRepository.InsertAsync(new IdentityLinkUser( + Guid.NewGuid(), + new IdentityLinkUserInfo(d, null), + new IdentityLinkUserInfo(c, null)), true); + + await IdentityLinkUserRepository.InsertAsync(new IdentityLinkUser( + Guid.NewGuid(), + new IdentityLinkUserInfo(e, null), + new IdentityLinkUserInfo(c, null)), true); + + await IdentityLinkUserRepository.InsertAsync(new IdentityLinkUser( + Guid.NewGuid(), + new IdentityLinkUserInfo(f, null), + new IdentityLinkUserInfo(e, null)), true); + + await IdentityLinkUserRepository.InsertAsync(new IdentityLinkUser( + Guid.NewGuid(), + new IdentityLinkUserInfo(g, null), + new IdentityLinkUserInfo(h, null)), true); + + await IdentityLinkUserRepository.InsertAsync(new IdentityLinkUser( + Guid.NewGuid(), + new IdentityLinkUserInfo(i, null), + new IdentityLinkUserInfo(h, null)), true); + + var linkUsers = await IdentityLinkUserManager.GetListAsync(new IdentityLinkUserInfo(a, null), includeIndirect: true); + linkUsers.Count.ShouldBe(5); + + linkUsers = await IdentityLinkUserManager.GetListAsync(new IdentityLinkUserInfo(f, null), includeIndirect: true); + linkUsers.Count.ShouldBe(5); + + linkUsers = await IdentityLinkUserManager.GetListAsync(new IdentityLinkUserInfo(g, null), includeIndirect: true); + linkUsers.Count.ShouldBe(2); + } + [Fact] public virtual async Task LinkAsync() { diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityLinkUserRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityLinkUserRepository_Tests.cs index e5dd6dcd5f..64dd9c5d20 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityLinkUserRepository_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityLinkUserRepository_Tests.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Shouldly; using Volo.Abp.Modularity; using Xunit; @@ -54,5 +55,26 @@ namespace Volo.Abp.Identity davidLinkUsers.ShouldContain(x => x.SourceUserId == john.Id && x.SourceTenantId == john.TenantId); davidLinkUsers.ShouldContain(x => x.TargetUserId == neo.Id && x.TargetTenantId == neo.TenantId); } + + [Fact] + public async Task DeleteAsync() + { + var john = await UserRepository.GetAsync(TestData.UserJohnId); + var david = await UserRepository.GetAsync(TestData.UserDavidId); + + (await IdentityLinkUserRepository.FindAsync( + new IdentityLinkUserInfo(john.Id, john.TenantId), + new IdentityLinkUserInfo(david.Id, david.TenantId))).ShouldNotBeNull(); + + await IdentityLinkUserRepository.DeleteAsync(new IdentityLinkUserInfo(david.Id, david.TenantId)); + + (await IdentityLinkUserRepository.FindAsync( + new IdentityLinkUserInfo(john.Id, john.TenantId), + new IdentityLinkUserInfo(david.Id, david.TenantId))).ShouldBeNull(); + + (await IdentityLinkUserRepository.FindAsync( + new IdentityLinkUserInfo(david.Id, david.TenantId), + new IdentityLinkUserInfo(john.Id, john.TenantId))).ShouldBeNull(); + } } }