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 98269da005..f5e7c14d37 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 @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Volo.Abp.Domain.Repositories; +using Volo.Abp.Identity.Organizations; namespace Volo.Abp.Identity { @@ -61,6 +62,11 @@ namespace Volo.Abp.Identity CancellationToken cancellationToken = default ); + Task> GetOrganizationUnitsAsync( + Guid id, + bool includeDetails = false, + CancellationToken cancellationToken = default); + Task GetCountAsync( string filter = null, CancellationToken cancellationToken = default diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs index 6b155aa574..78c7e629a4 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs @@ -300,7 +300,7 @@ namespace Volo.Abp.Identity { Check.NotNull(organizationUnitId, nameof(organizationUnitId)); - if (IsInOrganizationUnit(organizationUnitId)) + if (!IsInOrganizationUnit(organizationUnitId)) { return; } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs index f5a54bf961..87faf866cb 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs @@ -157,7 +157,7 @@ namespace Volo.Abp.Identity await CheckMaxUserOrganizationUnitMembershipCountAsync(user.TenantId, organizationUnitIds.Length); - var currentOus = user.OrganizationUnits; + var currentOus = await _identityUserRepository.GetOrganizationUnitsAsync(user.Id).ConfigureAwait(false); //Remove from removed OUs foreach (var currentOu in currentOus) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Organizations/IOrganizationUnitRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Organizations/IOrganizationUnitRepository.cs index 2d0987a855..9774534bfc 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Organizations/IOrganizationUnitRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Organizations/IOrganizationUnitRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; @@ -12,5 +13,13 @@ namespace Volo.Abp.Identity.Organizations Task> GetAllChildrenWithParentCodeAsync(string code, Guid? parentId); Task> GetListAsync(IEnumerable ids); + + Task> GetListAsync(bool includeDetails = true); + + Task GetOrganizationUnit(string displayName, bool includeDetails = false, CancellationToken cancellationToken = default); + + Task AddRole(OrganizationUnit ou, IdentityRole role, Guid? tenantId); + + Task RemoveRole(OrganizationUnit ou, IdentityRole role, Guid? tenantId); } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Organizations/OrganizationUnit.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Organizations/OrganizationUnit.cs index 2a48337e45..ca9e17267c 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Organizations/OrganizationUnit.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Organizations/OrganizationUnit.cs @@ -62,7 +62,6 @@ namespace Volo.Abp.Identity.Organizations TenantId = tenantId; DisplayName = displayName; ParentId = parentId; - Roles = new Collection(); } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Organizations/OrganizationUnitManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Organizations/OrganizationUnitManager.cs index 66861a9027..dbf6f44cef 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Organizations/OrganizationUnitManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Organizations/OrganizationUnitManager.cs @@ -40,13 +40,13 @@ namespace Volo.Abp.Identity.Organizations { organizationUnit.Code = await GetNextChildCodeAsync(organizationUnit.ParentId); await ValidateOrganizationUnitAsync(organizationUnit); - await _organizationUnitRepository.InsertAsync(organizationUnit); + await _organizationUnitRepository.InsertAsync(organizationUnit).ConfigureAwait(false); } public virtual async Task UpdateAsync(OrganizationUnit organizationUnit) { await ValidateOrganizationUnitAsync(organizationUnit); - await _organizationUnitRepository.UpdateAsync(organizationUnit); + await _organizationUnitRepository.UpdateAsync(organizationUnit).ConfigureAwait(false); } public virtual async Task GetNextChildCodeAsync(Guid? parentId) @@ -74,16 +74,16 @@ namespace Volo.Abp.Identity.Organizations foreach (var child in children) { - await _organizationUnitRepository.DeleteAsync(child); + await _organizationUnitRepository.DeleteAsync(child).ConfigureAwait(false); } - await _organizationUnitRepository.DeleteAsync(id); + await _organizationUnitRepository.DeleteAsync(id).ConfigureAwait(false); } [UnitOfWork] public virtual async Task MoveAsync(Guid id, Guid? parentId) { - var organizationUnit = await _organizationUnitRepository.GetAsync(id); + var organizationUnit = await _organizationUnitRepository.GetAsync(id).ConfigureAwait(false); if (organizationUnit.ParentId == parentId) { return; @@ -110,7 +110,7 @@ namespace Volo.Abp.Identity.Organizations public virtual async Task GetCodeOrDefaultAsync(Guid id) { - var ou = await _organizationUnitRepository.GetAsync(id); + var ou = await _organizationUnitRepository.GetAsync(id).ConfigureAwait(false); return ou?.Code; } @@ -130,12 +130,12 @@ namespace Volo.Abp.Identity.Organizations { if (!recursive) { - return await _organizationUnitRepository.GetChildrenAsync(parentId); + return await _organizationUnitRepository.GetChildrenAsync(parentId).ConfigureAwait(false); } if (!parentId.HasValue) { - return await _organizationUnitRepository.GetListAsync(); + return await _organizationUnitRepository.GetListAsync().ConfigureAwait(false); } var code = await GetCodeOrDefaultAsync(parentId.Value); @@ -156,16 +156,16 @@ namespace Volo.Abp.Identity.Organizations ); } - public virtual Task AddRoleToOrganizationUnitAsync(IdentityRole role, OrganizationUnit ou) + public virtual async Task AddRoleToOrganizationUnitAsync(IdentityRole role, OrganizationUnit ou) { var currentRoles = ou.Roles; if (currentRoles.Any(r => r.Id == role.Id)) { - return Task.FromResult(0); + return; } ou.AddRole(role.Id); - return Task.FromResult(0); + await _organizationUnitRepository.AddRole(ou, role, ou.TenantId); } public virtual async Task RemoveRoleFromOrganizationUnitAsync(Guid roleId, Guid ouId) @@ -178,8 +178,7 @@ namespace Volo.Abp.Identity.Organizations public virtual async Task RemoveRoleFromOrganizationUnitAsync(IdentityRole role, OrganizationUnit organizationUnit) { - await _organizationUnitRepository.EnsureCollectionLoadedAsync(organizationUnit, ou => ou.Roles, _cancellationTokenProvider.Token).ConfigureAwait(false); - + await _organizationUnitRepository.RemoveRole(organizationUnit, role, organizationUnit.TenantId); organizationUnit.RemoveRole(role.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 e3dfe6ee8c..f27ece7b25 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 @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Identity.Organizations; namespace Volo.Abp.Identity.EntityFrameworkCore { @@ -143,6 +144,19 @@ namespace Volo.Abp.Identity.EntityFrameworkCore .LongCountAsync(GetCancellationToken(cancellationToken)).ConfigureAwait(false); } + public virtual async Task> GetOrganizationUnitsAsync( + Guid id, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + var query = from userOU in DbContext.Set() + join ou in DbContext.OrganizationUnits.IncludeDetails(includeDetails) on userOU.OrganizationUnitId equals ou.Id + where userOU.UserId == id + select ou; + + return await query.ToListAsync(GetCancellationToken(cancellationToken)).ConfigureAwait(false); + } + public override IQueryable WithDetails() { return GetQueryable().IncludeDetails(); diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs index 37648adef8..b90d600c94 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; @@ -33,5 +34,46 @@ namespace Volo.Abp.Identity.EntityFrameworkCore { return await DbSet.Where(t => ids.Contains(t.Id)).ToListAsync(); } + + public async Task> GetListAsync(bool includeDetails = true) + { + return await DbSet + .IncludeDetails(includeDetails) + .ToListAsync(); + } + + public async Task GetOrganizationUnit(string displayName, bool includeDetails = false, CancellationToken cancellationToken = default) + { + return await DbSet + .IncludeDetails(includeDetails) + .FirstOrDefaultAsync( + ou => ou.DisplayName == displayName, + GetCancellationToken(cancellationToken) + ).ConfigureAwait(false); + } + + public async Task AddRole(OrganizationUnit ou, IdentityRole role, Guid? tenantId) + { + var our = new OrganizationUnitRole(tenantId, role.Id, ou.Id); + DbContext.Set().Add(our); + await DbContext.SaveChangesAsync(); + } + + public async Task RemoveRole(OrganizationUnit ou, IdentityRole role, Guid? tenantId) + { + var context = DbContext.Set(); + var our = await context.FirstOrDefaultAsync(our => + our.OrganizationUnitId == ou.Id && + our.RoleId == role.Id && + our.TenantId == tenantId + ); + DbContext.Set().Remove(our); + await DbContext.SaveChangesAsync(); + } + + public override IQueryable WithDetails() + { + return GetQueryable().IncludeDetails(); + } } } diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs index 20e15d1dff..78daa5217b 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs @@ -149,6 +149,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore b.Property(ou => ou.DisplayName).IsRequired().HasMaxLength(OrganizationUnitConsts.MaxDisplayNameLength).HasColumnName(nameof(OrganizationUnit.DisplayName)); b.HasMany(ou => ou.Children).WithOne().HasForeignKey(ou => ou.ParentId); + b.HasMany(ou => ou.Roles).WithOne().HasForeignKey(our => our.OrganizationUnitId).IsRequired(); b.HasIndex(ou => ou.ParentId); }); diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs index bc03f35c49..b2749a2f4a 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs @@ -4,8 +4,10 @@ using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Shouldly; +using Volo.Abp.Identity.Organizations; using Volo.Abp.Uow; using Xunit; +using System.Linq; namespace Volo.Abp.Identity { @@ -14,6 +16,7 @@ namespace Volo.Abp.Identity private readonly IdentityUserManager _identityUserManager; private readonly IIdentityUserRepository _identityUserRepository; private readonly IIdentityRoleRepository _identityRoleRepository; + private readonly IOrganizationUnitRepository _organizationUnitRepository; private readonly ILookupNormalizer _lookupNormalizer; private readonly IUnitOfWorkManager _unitOfWorkManager; private readonly IdentityTestData _testData; @@ -23,6 +26,7 @@ namespace Volo.Abp.Identity _identityUserManager = GetRequiredService(); _identityUserRepository = GetRequiredService(); _identityRoleRepository = GetRequiredService(); + _organizationUnitRepository = GetRequiredService(); _lookupNormalizer = GetRequiredService(); _testData = GetRequiredService(); _unitOfWorkManager = GetRequiredService(); @@ -83,5 +87,60 @@ namespace Volo.Abp.Identity await uow.CompleteAsync().ConfigureAwait(false); } } + + [Fact] + public async Task SetOrganizationUnitsAsync() + { + using (var uow = _unitOfWorkManager.Begin()) + { + var user = await _identityUserRepository.FindByNormalizedUserNameAsync( + _lookupNormalizer.NormalizeName("david")).ConfigureAwait(false); + user.ShouldNotBeNull(); + + var ou = await _organizationUnitRepository.GetOrganizationUnit( + _lookupNormalizer.NormalizeName("OU11")).ConfigureAwait(false); + ou.ShouldNotBeNull(); + + await _identityUserManager.SetOrganizationUnitsAsync(user, new Guid[] + { + ou.Id + }).ConfigureAwait(false); + + user = await _identityUserRepository.FindByNormalizedUserNameAsync( + _lookupNormalizer.NormalizeName("david")).ConfigureAwait(false); + user.OrganizationUnits.Count.ShouldBeGreaterThan(0); + user.OrganizationUnits.FirstOrDefault(uou => uou.OrganizationUnitId == ou.Id).ShouldNotBeNull(); + + await uow.CompleteAsync().ConfigureAwait(false); + } + } + + [Fact] + public async Task SetOrganizationUnits_Should_Remove() + { + using (var uow = _unitOfWorkManager.Begin()) + { + var ou = await _organizationUnitRepository.GetOrganizationUnit( + _lookupNormalizer.NormalizeName("OU111")).ConfigureAwait(false); + ou.ShouldNotBeNull(); + + var user = await _identityUserRepository.FindByNormalizedUserNameAsync( + _lookupNormalizer.NormalizeName("john.nash")).ConfigureAwait(false); + user.ShouldNotBeNull(); + + var ouNew = await _organizationUnitRepository.GetOrganizationUnit( + _lookupNormalizer.NormalizeName("OU2")).ConfigureAwait(false); + ouNew.ShouldNotBeNull(); + + await _identityUserManager.SetOrganizationUnitsAsync(user, new Guid[] + { + ouNew.Id + }).ConfigureAwait(false); + + user.OrganizationUnits.ShouldNotContain(x => x.OrganizationUnitId == ou.Id); + + await uow.CompleteAsync().ConfigureAwait(false); + } + } } } diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/OrganizationUnitManager_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/OrganizationUnitManager_Tests.cs new file mode 100644 index 0000000000..f19716b8ee --- /dev/null +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/OrganizationUnitManager_Tests.cs @@ -0,0 +1,111 @@ +using Microsoft.AspNetCore.Identity; +using Shouldly; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Identity.Organizations; +using Xunit; + +namespace Volo.Abp.Identity +{ + public class OrganizationUnitManager_Tests : AbpIdentityDomainTestBase + { + private readonly OrganizationUnitManager _organizationUnitManager; + private readonly IOrganizationUnitRepository _organizationUnitRepository; + private readonly IdentityTestData _testData; + private readonly IIdentityRoleRepository _identityRoleRepository; + private readonly ILookupNormalizer _lookupNormalizer; + public OrganizationUnitManager_Tests() + { + _organizationUnitManager = GetRequiredService(); + _organizationUnitRepository = GetRequiredService(); + _identityRoleRepository = GetRequiredService(); + _lookupNormalizer = GetRequiredService(); + _testData = GetRequiredService(); + } + + [Fact] + public async Task CreateAsnyc() + { + await _organizationUnitManager.CreateAsync(new OrganizationUnit(null, "Root 1")); + + var root1 = await _organizationUnitRepository.GetOrganizationUnit("Root 1").ConfigureAwait(false); + root1.ShouldNotBeNull(); + } + + [Fact] + public async Task UpdateAsync() + { + var ou = await _organizationUnitRepository.GetOrganizationUnit("OU111").ConfigureAwait(false); + ou.Code = OrganizationUnit.CreateCode(123); + await _organizationUnitManager.UpdateAsync(ou); + + var ouAfterChange = await _organizationUnitRepository.GetOrganizationUnit("OU111").ConfigureAwait(false); + ouAfterChange.Code.ShouldContain("123"); + } + + [Fact] + public async Task DeleteAsync() + { + var ou = await _organizationUnitRepository.GetOrganizationUnit("OU11").ConfigureAwait(false); + await _organizationUnitManager.DeleteAsync(ou.Id); + + (await _organizationUnitRepository.GetOrganizationUnit("OU11").ConfigureAwait(false)).ShouldBeNull(); + } + + [Fact] + public async Task MoveAsync() + { + var ou1 = await _organizationUnitRepository.GetOrganizationUnit("OU1").ConfigureAwait(false); + var ou2 = await _organizationUnitRepository.GetOrganizationUnit("OU2").ConfigureAwait(false); + + await _organizationUnitManager.MoveAsync(ou1.Id, ou2.Id); + + ou1 = await _organizationUnitRepository.GetOrganizationUnit("OU1").ConfigureAwait(false); + ou1.ParentId.ShouldBe(ou2.Id); + ou1.Code.ShouldBe(OrganizationUnit.CreateCode(2, 2)); + + var ou11 = await _organizationUnitRepository.GetOrganizationUnit("OU11").ConfigureAwait(false); + ou11.ParentId.ShouldBe(ou1.Id); + ou11.Code.ShouldBe(OrganizationUnit.CreateCode(2, 2, 1)); + + var ou111 = await _organizationUnitRepository.GetOrganizationUnit("OU111").ConfigureAwait(false); + ou111.ParentId.ShouldBe(ou11.Id); + ou111.Code.ShouldBe(OrganizationUnit.CreateCode(2, 2, 1, 1)); + + var ou112 = await _organizationUnitRepository.GetOrganizationUnit("OU112").ConfigureAwait(false); + ou112.ParentId.ShouldBe(ou11.Id); + ou112.Code.ShouldBe(OrganizationUnit.CreateCode(2, 2, 1, 2)); + + var ou12 = await _organizationUnitRepository.GetOrganizationUnit("OU12").ConfigureAwait(false); + ou12.ParentId.ShouldBe(ou1.Id); + ou12.Code.ShouldBe(OrganizationUnit.CreateCode(2, 2, 2)); + } + + [Fact] + public async Task AddRoleToOrganizationUnitAsync() + { + var ou = await _organizationUnitRepository.GetOrganizationUnit("OU1", true).ConfigureAwait(false); + var adminRole = await _identityRoleRepository.FindByNormalizedNameAsync(_lookupNormalizer.NormalizeName("admin")).ConfigureAwait(false); + await _organizationUnitManager.AddRoleToOrganizationUnitAsync(adminRole.Id, ou.Id); + + //TODO: This method has a bug: includeDetails not work + ou = await _organizationUnitRepository.GetOrganizationUnit("OU1", includeDetails: true).ConfigureAwait(false); + ou.Roles.FirstOrDefault().Id.ShouldBe(adminRole.Id); + } + + [Fact] + public async Task RemoveRoleFromOrganizationUnitAsync() + { + var ou = await _organizationUnitRepository.GetOrganizationUnit("OU1", true).ConfigureAwait(false); + var adminRole = await _identityRoleRepository.FindByNormalizedNameAsync(_lookupNormalizer.NormalizeName("admin")).ConfigureAwait(false); + await _organizationUnitManager.AddRoleToOrganizationUnitAsync(adminRole.Id, ou.Id); + + await _organizationUnitManager.RemoveRoleFromOrganizationUnitAsync(adminRole.Id, ou.Id); + ou = await _organizationUnitRepository.GetOrganizationUnit("OU1", includeDetails: true).ConfigureAwait(false); + ou.Roles.FirstOrDefault(r => r.RoleId == adminRole.Id).ShouldBeNull(); + } + } +} diff --git a/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/OrganizationUnitRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/OrganizationUnitRepository_Tests.cs new file mode 100644 index 0000000000..8fa9a14dba --- /dev/null +++ b/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/OrganizationUnitRepository_Tests.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Volo.Abp.Identity.EntityFrameworkCore +{ + public class OrganizationUnitRepository_Tests : OrganizationUnitRepository_Tests + { + } +} diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs index ad36fe69a7..979fcf940f 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs @@ -1,8 +1,10 @@ -using System.Security.Claims; +using System; +using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Volo.Abp.DependencyInjection; using Volo.Abp.Guids; +using Volo.Abp.Identity.Organizations; namespace Volo.Abp.Identity { @@ -12,20 +14,26 @@ namespace Volo.Abp.Identity private readonly IIdentityUserRepository _userRepository; private readonly IIdentityClaimTypeRepository _identityClaimTypeRepository; private readonly IIdentityRoleRepository _roleRepository; + private readonly IOrganizationUnitRepository _organizationUnitRepository; private readonly ILookupNormalizer _lookupNormalizer; private readonly IdentityTestData _testData; + private readonly OrganizationUnitManager _organizationUnitManager; private IdentityRole _adminRole; private IdentityRole _moderator; private IdentityRole _supporterRole; + private OrganizationUnit _ou111; + private OrganizationUnit _ou112; public AbpIdentityTestDataBuilder( IGuidGenerator guidGenerator, IIdentityUserRepository userRepository, IIdentityClaimTypeRepository identityClaimTypeRepository, IIdentityRoleRepository roleRepository, + IOrganizationUnitRepository organizationUnitRepository, ILookupNormalizer lookupNormalizer, - IdentityTestData testData) + IdentityTestData testData, + OrganizationUnitManager organizationUnitManager) { _guidGenerator = guidGenerator; _userRepository = userRepository; @@ -33,11 +41,14 @@ namespace Volo.Abp.Identity _roleRepository = roleRepository; _lookupNormalizer = lookupNormalizer; _testData = testData; + _organizationUnitRepository = organizationUnitRepository; + _organizationUnitManager = organizationUnitManager; } public async Task Build() { await AddRoles().ConfigureAwait(false); + await AddOrganizationUnits().ConfigureAwait(false); await AddUsers().ConfigureAwait(false); await AddClaimTypes().ConfigureAwait(false); } @@ -54,6 +65,31 @@ namespace Volo.Abp.Identity await _roleRepository.InsertAsync(_supporterRole).ConfigureAwait(false); } + /* Creates OU tree as shown below: + * + * - OU1 + * - OU11 + * - OU111 + * - OU112 + * - OU12 + * - OU2 + * - OU21 + */ + private async Task AddOrganizationUnits() + { + var ou1 = await CreateOU("OU1", OrganizationUnit.CreateCode(1)).ConfigureAwait(false); + var ou11 = await CreateOU("OU11", OrganizationUnit.CreateCode(1, 1), ou1.Id).ConfigureAwait(false); + _ou112 = await CreateOU("OU112", OrganizationUnit.CreateCode(1, 1, 2), ou11.Id).ConfigureAwait(false); + var ou12 = await CreateOU("OU12", OrganizationUnit.CreateCode(1, 2), ou1.Id).ConfigureAwait(false); + var ou2 = await CreateOU("OU2", OrganizationUnit.CreateCode(2)).ConfigureAwait(false); + var ou21 = await CreateOU("OU21", OrganizationUnit.CreateCode(2, 1), ou2.Id).ConfigureAwait(false); + + _ou111 = new OrganizationUnit(null, "OU111", ou11.Id); + _ou111.Code = OrganizationUnit.CreateCode(1, 1, 1); + _ou111.AddRole(_moderator.Id); + await _organizationUnitRepository.InsertAsync(_ou111).ConfigureAwait(false); + } + private async Task AddUsers() { var adminUser = new IdentityUser(_guidGenerator.Create(), "administrator", "admin@abp.io"); @@ -64,6 +100,8 @@ namespace Volo.Abp.Identity var john = new IdentityUser(_testData.UserJohnId, "john.nash", "john.nash@abp.io"); john.AddRole(_moderator.Id); john.AddRole(_supporterRole.Id); + john.AddOrganizationUnit(_ou111.Id); + john.AddOrganizationUnit(_ou112.Id); john.AddLogin(new UserLoginInfo("github", "john", "John Nash")); john.AddLogin(new UserLoginInfo("twitter", "johnx", "John Nash")); john.AddClaim(_guidGenerator, new Claim("TestClaimType", "42")); @@ -79,12 +117,19 @@ namespace Volo.Abp.Identity await _userRepository.InsertAsync(neo).ConfigureAwait(false); } + private async Task AddClaimTypes() { - var ageClaim = new IdentityClaimType(_testData.AgeClaimId, "Age", false, false, null, null, null,IdentityClaimValueType.Int); + var ageClaim = new IdentityClaimType(_testData.AgeClaimId, "Age", false, false, null, null, null, IdentityClaimValueType.Int); await _identityClaimTypeRepository.InsertAsync(ageClaim).ConfigureAwait(false); var educationClaim = new IdentityClaimType(_testData.EducationClaimId, "Education", true, false, null, null, null); await _identityClaimTypeRepository.InsertAsync(educationClaim).ConfigureAwait(false); } + + private async Task CreateOU(string displayName, string code, Guid? parentId = null) + { + var ou = await _organizationUnitRepository.InsertAsync(new OrganizationUnit(null, displayName, parentId) { Code = code }).ConfigureAwait(false); + return ou; + } } } \ No newline at end of file diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs index 98f25f6aee..f4719a63a7 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs @@ -144,6 +144,9 @@ namespace Volo.Abp.Identity john.Tokens.ShouldNotBeNull(); john.Tokens.Any().ShouldBeTrue(); + + john.OrganizationUnits.ShouldNotBeNull(); + john.OrganizationUnits.Any().ShouldBeTrue(); } } } diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/Identity_Repository_Resolve_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/Identity_Repository_Resolve_Tests.cs index 8b748f10b7..480e72f4cc 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/Identity_Repository_Resolve_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/Identity_Repository_Resolve_Tests.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp.Domain.Repositories; +using Volo.Abp.Identity.Organizations; using Volo.Abp.Modularity; using Xunit; @@ -20,6 +21,10 @@ namespace Volo.Abp.Identity ServiceProvider.GetService>().ShouldNotBeNull(); ServiceProvider.GetService>().ShouldNotBeNull(); ServiceProvider.GetService().ShouldNotBeNull(); + + ServiceProvider.GetService>().ShouldNotBeNull(); + ServiceProvider.GetService>().ShouldNotBeNull(); + ServiceProvider.GetService().ShouldNotBeNull(); } } } diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/LazyLoading_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/LazyLoading_Tests.cs index 3313803436..cd09c1209e 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/LazyLoading_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/LazyLoading_Tests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using Shouldly; +using Volo.Abp.Identity.Organizations; using Volo.Abp.Modularity; using Volo.Abp.Uow; using Xunit; @@ -14,6 +15,7 @@ namespace Volo.Abp.Identity { protected IIdentityUserRepository UserRepository { get; } protected IIdentityRoleRepository RoleRepository { get; } + protected IOrganizationUnitRepository OrganizationUnitRepository { get; } protected ILookupNormalizer LookupNormalizer { get; } protected LazyLoading_Tests() @@ -21,6 +23,7 @@ namespace Volo.Abp.Identity UserRepository = ServiceProvider.GetRequiredService(); RoleRepository = ServiceProvider.GetRequiredService(); LookupNormalizer = ServiceProvider.GetRequiredService(); + OrganizationUnitRepository = ServiceProvider.GetRequiredService(); } [Fact] @@ -55,6 +58,22 @@ namespace Volo.Abp.Identity john.Tokens.ShouldNotBeNull(); john.Tokens.Any().ShouldBeTrue(); + john.OrganizationUnits.ShouldNotBeNull(); + john.OrganizationUnits.Any().ShouldBeTrue(); + + await uow.CompleteAsync().ConfigureAwait(false); + } + } + + [Fact] + public async Task Should_Lazy_Load_OrganizationUnit_Collections() + { + using (var uow = GetRequiredService().Begin()) + { + var ou = await OrganizationUnitRepository.GetOrganizationUnit(LookupNormalizer.NormalizeName("OU111"), includeDetails: false).ConfigureAwait(false); + ou.Roles.ShouldNotBeNull(); //? + ou.Roles.Any().ShouldBeTrue(); + await uow.CompleteAsync().ConfigureAwait(false); } } diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs new file mode 100644 index 0000000000..4d6e07f447 --- /dev/null +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs @@ -0,0 +1,76 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Guids; +using Volo.Abp.Identity.Organizations; +using Volo.Abp.Modularity; +using Xunit; + +namespace Volo.Abp.Identity +{ + public abstract class OrganizationUnitRepository_Tests : AbpIdentityTestBase + where TStartupModule : IAbpModule + { + protected IOrganizationUnitRepository OrganizationUnitRepository { get; } + protected ILookupNormalizer LookupNormalizer { get; } + private readonly IdentityTestData _testData; + private readonly IGuidGenerator _guidGenerator; + + public OrganizationUnitRepository_Tests() + { + OrganizationUnitRepository = ServiceProvider.GetRequiredService(); + LookupNormalizer = ServiceProvider.GetRequiredService(); + _testData = GetRequiredService(); + _guidGenerator = GetRequiredService(); + } + + [Fact] + public async Task GetChildrenAsync() + { + (await OrganizationUnitRepository.GetChildrenAsync(_testData.RoleModeratorId).ConfigureAwait(false)).ShouldNotBeNull(); + } + + [Fact] + public async Task GetAllChildrenWithParentCodeAsync() + { + (await OrganizationUnitRepository.GetAllChildrenWithParentCodeAsync(OrganizationUnit.CreateCode(0), _guidGenerator.Create()).ConfigureAwait(false)).ShouldNotBeNull(); + } + + [Fact] + public async Task GetListAsync() + { + var ouIds = (await OrganizationUnitRepository.GetListAsync().ConfigureAwait(false)) + .Select(ou => ou.Id).Take(2); + var ous = await OrganizationUnitRepository.GetListAsync(ouIds).ConfigureAwait(false); + ous.Count.ShouldBe(2); + ous.ShouldContain(ou => ou.Id == ouIds.First()); + } + + [Fact] + public async Task GetOrganizationUnit() + { + var organizationUnit = await OrganizationUnitRepository.GetOrganizationUnit("OU111").ConfigureAwait(false); + organizationUnit.ShouldNotBeNull(); + } + + [Fact] + public async Task GetCountAsync() + { + (await OrganizationUnitRepository.GetCountAsync().ConfigureAwait(false)).ShouldBeGreaterThan(0); + } + + [Fact] + public async Task Should_Eager_Load_OrganizationUnit_Collections() + { + var ou = (await OrganizationUnitRepository.GetListAsync(true).ConfigureAwait(false)) + .FirstOrDefault(ou => ou.DisplayName == "OU111"); + ou.Roles.ShouldNotBeNull(); + ou.Roles.Any().ShouldBeTrue(); + } + } +}