mirror of https://github.com/abpframework/abp.git
committed by
GitHub
15 changed files with 862 additions and 12 deletions
@ -1,7 +1,11 @@ |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.EventBus.Abstractions; |
|||
|
|||
namespace Volo.Abp.Gdpr; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpEventBusAbstractionsModule) |
|||
)] |
|||
public class AbpGdprAbstractionsModule : AbpModule |
|||
{ |
|||
} |
|||
@ -1,9 +1,18 @@ |
|||
using System; |
|||
using Volo.Abp.EventBus; |
|||
|
|||
namespace Volo.Abp.Gdpr; |
|||
|
|||
[Serializable] |
|||
public class GdprUserDataDeletionRequestedEto |
|||
public class GdprUserDataDeletionRequestedEto : IEventDataMayHaveTenantId |
|||
{ |
|||
public Guid? TenantId { get; set; } |
|||
|
|||
public Guid UserId { get; set; } |
|||
} |
|||
|
|||
public bool IsMultiTenant(out Guid? tenantId) |
|||
{ |
|||
tenantId = TenantId; |
|||
return TenantId.HasValue; |
|||
} |
|||
} |
|||
|
|||
@ -1,13 +1,22 @@ |
|||
using System; |
|||
using Volo.Abp.EventBus; |
|||
|
|||
namespace Volo.Abp.Gdpr; |
|||
|
|||
[Serializable] |
|||
public class GdprUserDataPreparedEto |
|||
public class GdprUserDataPreparedEto : IEventDataMayHaveTenantId |
|||
{ |
|||
public Guid? TenantId { get; set; } |
|||
|
|||
public Guid RequestId { get; set; } |
|||
|
|||
public string Provider { get; set; } = default!; |
|||
|
|||
public GdprDataInfo Data { get; set; } = default!; |
|||
} |
|||
|
|||
public bool IsMultiTenant(out Guid? tenantId) |
|||
{ |
|||
tenantId = TenantId; |
|||
return TenantId.HasValue; |
|||
} |
|||
} |
|||
|
|||
@ -1,11 +1,20 @@ |
|||
using System; |
|||
using Volo.Abp.EventBus; |
|||
|
|||
namespace Volo.Abp.Gdpr; |
|||
|
|||
[Serializable] |
|||
public class GdprUserDataRequestedEto |
|||
public class GdprUserDataRequestedEto : IEventDataMayHaveTenantId |
|||
{ |
|||
public Guid? TenantId { get; set; } |
|||
|
|||
public Guid UserId { get; set; } |
|||
|
|||
public Guid RequestId { get; set; } |
|||
} |
|||
|
|||
public bool IsMultiTenant(out Guid? tenantId) |
|||
{ |
|||
tenantId = TenantId; |
|||
return TenantId.HasValue; |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,52 @@ |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Entities.Events; |
|||
using Volo.Abp.EventBus; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace Volo.Abp.Identity; |
|||
|
|||
//Sessions, user delegations and link users have no navigation from IdentityUser,
|
|||
//so clearing the user's collections doesn't cover them.
|
|||
public class UserDeletedEventHandler : |
|||
ILocalEventHandler<EntityDeletedEventData<IdentityUser>>, |
|||
ITransientDependency |
|||
{ |
|||
protected IIdentitySessionRepository IdentitySessionRepository { get; } |
|||
protected IIdentityUserDelegationRepository IdentityUserDelegationRepository { get; } |
|||
protected IIdentityLinkUserRepository IdentityLinkUserRepository { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
|
|||
public UserDeletedEventHandler( |
|||
IIdentitySessionRepository identitySessionRepository, |
|||
IIdentityUserDelegationRepository identityUserDelegationRepository, |
|||
IIdentityLinkUserRepository identityLinkUserRepository, |
|||
ICurrentTenant currentTenant) |
|||
{ |
|||
IdentitySessionRepository = identitySessionRepository; |
|||
IdentityUserDelegationRepository = identityUserDelegationRepository; |
|||
IdentityLinkUserRepository = identityLinkUserRepository; |
|||
CurrentTenant = currentTenant; |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public virtual async Task HandleEventAsync(EntityDeletedEventData<IdentityUser> eventData) |
|||
{ |
|||
var user = eventData.Entity; |
|||
|
|||
await IdentitySessionRepository.DeleteAllAsync(user.Id); |
|||
|
|||
var delegations = await IdentityUserDelegationRepository.GetListAsync(sourceUserId: user.Id, targetUserId: null); |
|||
delegations.AddRange(await IdentityUserDelegationRepository.GetListAsync(sourceUserId: null, targetUserId: user.Id)); |
|||
//A delegation of the user to itself is returned by both queries.
|
|||
await IdentityUserDelegationRepository.DeleteManyAsync(delegations.DistinctBy(x => x.Id).ToList()); |
|||
|
|||
//Link users are stored in the host database.
|
|||
using (CurrentTenant.Change(null)) |
|||
{ |
|||
await IdentityLinkUserRepository.DeleteAsync(new IdentityLinkUserInfo(user.Id, user.TenantId)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
namespace Volo.Abp.Identity; |
|||
|
|||
public class IdentityUserManager_Delete_Tests : IdentityUserManager_Delete_Tests<AbpIdentityDomainTestModule> |
|||
{ |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
namespace Volo.Abp.Identity; |
|||
|
|||
public class OrganizationUnitManager_CreateMany_Tests : OrganizationUnitManager_CreateMany_Tests<AbpIdentityDomainTestModule> |
|||
{ |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Identity.MongoDB; |
|||
|
|||
[Collection(MongoTestCollection.Name)] |
|||
public class IdentityUserManager_Delete_Tests : IdentityUserManager_Delete_Tests<AbpIdentityMongoDbTestModule> |
|||
{ |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Identity.MongoDB; |
|||
|
|||
[Collection(MongoTestCollection.Name)] |
|||
public class OrganizationUnitManager_CreateMany_Tests : OrganizationUnitManager_CreateMany_Tests<AbpIdentityMongoDbTestModule> |
|||
{ |
|||
} |
|||
@ -0,0 +1,477 @@ |
|||
using System; |
|||
using System.Security.Claims; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Identity; |
|||
using Shouldly; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.Domain.Entities; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Security.Claims; |
|||
using Volo.Abp.Uow; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Identity; |
|||
|
|||
public abstract class IdentityUserManager_Delete_Tests<TStartupModule> : AbpIdentityTestBase<TStartupModule> |
|||
where TStartupModule : IAbpModule |
|||
{ |
|||
protected IdentityUserManager IdentityUserManager { get; } |
|||
protected IIdentityUserRepository IdentityUserRepository { get; } |
|||
protected IOrganizationUnitRepository OrganizationUnitRepository { get; } |
|||
protected IdentityLinkUserManager IdentityLinkUserManager { get; } |
|||
protected IIdentitySessionRepository IdentitySessionRepository { get; } |
|||
protected IIdentityUserDelegationRepository IdentityUserDelegationRepository { get; } |
|||
protected ILookupNormalizer LookupNormalizer { get; } |
|||
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
|||
protected IDataFilter DataFilter { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected IDistributedCache<AbpDynamicClaimCacheItem> DynamicClaimCache { get; } |
|||
|
|||
protected IdentityUserManager_Delete_Tests() |
|||
{ |
|||
CurrentTenant = GetRequiredService<ICurrentTenant>(); |
|||
DynamicClaimCache = GetRequiredService<IDistributedCache<AbpDynamicClaimCacheItem>>(); |
|||
IdentityUserManager = GetRequiredService<IdentityUserManager>(); |
|||
IdentityUserRepository = GetRequiredService<IIdentityUserRepository>(); |
|||
OrganizationUnitRepository = GetRequiredService<IOrganizationUnitRepository>(); |
|||
IdentityLinkUserManager = GetRequiredService<IdentityLinkUserManager>(); |
|||
IdentitySessionRepository = GetRequiredService<IIdentitySessionRepository>(); |
|||
IdentityUserDelegationRepository = GetRequiredService<IIdentityUserDelegationRepository>(); |
|||
LookupNormalizer = GetRequiredService<ILookupNormalizer>(); |
|||
UnitOfWorkManager = GetRequiredService<IUnitOfWorkManager>(); |
|||
DataFilter = GetRequiredService<IDataFilter>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public virtual async Task DeleteAsync_Should_Remove_All_Related_Data() |
|||
{ |
|||
var userId = Guid.NewGuid(); |
|||
var linkedUserId = Guid.NewGuid(); |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
(await IdentityUserManager.CreateAsync( |
|||
new IdentityUser(userId, $"delete-{userId:N}", $"delete-{userId:N}@abp.io"))).CheckErrors(); |
|||
(await IdentityUserManager.CreateAsync( |
|||
new IdentityUser(linkedUserId, $"linked-{linkedUserId:N}", $"linked-{linkedUserId:N}@abp.io"))).CheckErrors(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
var user = await IdentityUserManager.GetByIdAsync(userId); |
|||
|
|||
await IdentityUserManager.AddClaimAsync(user, new Claim("test", "test")); |
|||
await IdentityUserManager.AddLoginAsync(user, new UserLoginInfo("test", "test", "test")); |
|||
await IdentityUserManager.AddToRoleAsync(user, "moderator"); |
|||
user.SetToken("test", "test", "test"); |
|||
user.AddPasswordHistory("test"); |
|||
user.AddPasskey([1, 2, 3], new IdentityPasskeyData()); |
|||
await IdentityUserManager.AddToOrganizationUnitAsync( |
|||
user, |
|||
await OrganizationUnitRepository.GetAsync(LookupNormalizer.NormalizeName("OU11"))); |
|||
await IdentityLinkUserManager.LinkAsync( |
|||
new IdentityLinkUserInfo(userId), |
|||
new IdentityLinkUserInfo(linkedUserId)); |
|||
|
|||
await IdentitySessionRepository.InsertAsync(new IdentitySession( |
|||
Guid.NewGuid(), $"session-{userId:N}", "Web", "Chrome", userId, null, "MyApp", "127.0.0.1", DateTime.UtcNow)); |
|||
await IdentityUserDelegationRepository.InsertAsync(new IdentityUserDelegation( |
|||
Guid.NewGuid(), userId, linkedUserId, DateTime.UtcNow, DateTime.UtcNow.AddDays(1))); |
|||
await IdentityUserDelegationRepository.InsertAsync(new IdentityUserDelegation( |
|||
Guid.NewGuid(), linkedUserId, userId, DateTime.UtcNow, DateTime.UtcNow.AddDays(1))); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
var user = await IdentityUserManager.GetByIdAsync(userId); |
|||
|
|||
user.Claims.Count.ShouldBeGreaterThan(0); |
|||
user.Logins.Count.ShouldBeGreaterThan(0); |
|||
user.Roles.Count.ShouldBeGreaterThan(0); |
|||
user.Tokens.Count.ShouldBeGreaterThan(0); |
|||
user.OrganizationUnits.Count.ShouldBeGreaterThan(0); |
|||
user.PasswordHistories.Count.ShouldBeGreaterThan(0); |
|||
user.Passkeys.Count.ShouldBeGreaterThan(0); |
|||
|
|||
(await IdentityUserManager.DeleteAsync(user)).CheckErrors(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
//The user is soft deleted, disable the filter to see what is left behind.
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
using (DataFilter.Disable<ISoftDelete>()) |
|||
{ |
|||
var deletedUser = await IdentityUserRepository.FindAsync(userId); |
|||
deletedUser.ShouldNotBeNull(); |
|||
|
|||
deletedUser.Claims.Count.ShouldBe(0); |
|||
deletedUser.Logins.Count.ShouldBe(0); |
|||
deletedUser.Roles.Count.ShouldBe(0); |
|||
deletedUser.Tokens.Count.ShouldBe(0); |
|||
deletedUser.OrganizationUnits.Count.ShouldBe(0); |
|||
deletedUser.PasswordHistories.Count.ShouldBe(0); |
|||
deletedUser.Passkeys.Count.ShouldBe(0); |
|||
|
|||
(await IdentityLinkUserManager.IsLinkedAsync( |
|||
new IdentityLinkUserInfo(userId), |
|||
new IdentityLinkUserInfo(linkedUserId))).ShouldBeFalse(); |
|||
|
|||
(await IdentitySessionRepository.GetCountAsync(userId: userId)).ShouldBe(0); |
|||
(await IdentityUserDelegationRepository.GetListAsync(sourceUserId: userId, targetUserId: null)).ShouldBeEmpty(); |
|||
(await IdentityUserDelegationRepository.GetListAsync(sourceUserId: null, targetUserId: userId)).ShouldBeEmpty(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public virtual async Task DeleteAsync_Should_Remove_Related_Data_Of_A_User_Loaded_Without_Details() |
|||
{ |
|||
var userId = Guid.NewGuid(); |
|||
var userName = $"no-details-{userId:N}"; |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
(await IdentityUserManager.CreateAsync( |
|||
new IdentityUser(userId, userName, $"{userName}@abp.io"))).CheckErrors(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
var user = await IdentityUserManager.GetByIdAsync(userId); |
|||
|
|||
(await IdentityUserManager.AddClaimAsync(user, new Claim("test", "test"))).CheckErrors(); |
|||
(await IdentityUserManager.AddLoginAsync(user, new UserLoginInfo("test", "test", "test"))).CheckErrors(); |
|||
(await IdentityUserManager.AddToRoleAsync(user, "moderator")).CheckErrors(); |
|||
user.SetToken("test", "test", "test"); |
|||
user.AddPasswordHistory("test"); |
|||
user.AddPasskey([1, 2, 3], new IdentityPasskeyData()); |
|||
await IdentityUserManager.AddToOrganizationUnitAsync( |
|||
user, |
|||
await OrganizationUnitRepository.GetAsync(LookupNormalizer.NormalizeName("OU11"))); |
|||
await IdentityUserRepository.UpdateAsync(user); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
var user = await IdentityUserRepository.FindByNormalizedUserNameAsync( |
|||
LookupNormalizer.NormalizeName(userName), |
|||
includeDetails: false); |
|||
|
|||
(await IdentityUserManager.DeleteAsync(user)).CheckErrors(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
//Every collection of the user has to be loaded before it is cleared.
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
using (DataFilter.Disable<ISoftDelete>()) |
|||
{ |
|||
var deletedUser = await IdentityUserRepository.FindAsync(userId); |
|||
deletedUser.ShouldNotBeNull(); |
|||
|
|||
deletedUser.Claims.Count.ShouldBe(0); |
|||
deletedUser.Logins.Count.ShouldBe(0); |
|||
deletedUser.Roles.Count.ShouldBe(0); |
|||
deletedUser.Tokens.Count.ShouldBe(0); |
|||
deletedUser.OrganizationUnits.Count.ShouldBe(0); |
|||
deletedUser.PasswordHistories.Count.ShouldBe(0); |
|||
deletedUser.Passkeys.Count.ShouldBe(0); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public virtual async Task Deleting_A_User_Through_The_Repository_Should_Remove_Sessions_Delegations_And_Links() |
|||
{ |
|||
var userId = Guid.NewGuid(); |
|||
var linkedUserId = Guid.NewGuid(); |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
(await IdentityUserManager.CreateAsync( |
|||
new IdentityUser(userId, $"repo-{userId:N}", $"repo-{userId:N}@abp.io"))).CheckErrors(); |
|||
(await IdentityUserManager.CreateAsync( |
|||
new IdentityUser(linkedUserId, $"repo-linked-{linkedUserId:N}", $"repo-linked-{linkedUserId:N}@abp.io"))).CheckErrors(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
await IdentityLinkUserManager.LinkAsync( |
|||
new IdentityLinkUserInfo(userId), |
|||
new IdentityLinkUserInfo(linkedUserId)); |
|||
|
|||
await IdentitySessionRepository.InsertAsync(new IdentitySession( |
|||
Guid.NewGuid(), $"repo-session-{userId:N}", "Web", "Chrome", userId, null, "MyApp", "127.0.0.1", DateTime.UtcNow)); |
|||
await IdentityUserDelegationRepository.InsertAsync(new IdentityUserDelegation( |
|||
Guid.NewGuid(), userId, linkedUserId, DateTime.UtcNow, DateTime.UtcNow.AddDays(1))); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
//Custom code may delete the user without using IdentityUserManager.
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
await IdentityUserRepository.DeleteAsync(await IdentityUserRepository.GetAsync(userId)); |
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
(await IdentitySessionRepository.GetCountAsync(userId: userId)).ShouldBe(0); |
|||
(await IdentityUserDelegationRepository.GetListAsync(sourceUserId: userId, targetUserId: null)).ShouldBeEmpty(); |
|||
(await IdentityLinkUserManager.IsLinkedAsync( |
|||
new IdentityLinkUserInfo(userId), |
|||
new IdentityLinkUserInfo(linkedUserId))).ShouldBeFalse(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public virtual async Task Should_Delete_A_User_That_Does_Not_Pass_The_User_Validators() |
|||
{ |
|||
var userId = Guid.NewGuid(); |
|||
|
|||
//Insert with the repository, so the user name is not validated.
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
var user = new IdentityUser(userId, $"invalid user name {userId:N}", $"invalid-{userId:N}@abp.io"); |
|||
user.AddPasswordHistory("test"); |
|||
await IdentityUserRepository.InsertAsync(user); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
(await IdentityUserManager.DeleteAsync(await IdentityUserRepository.GetAsync(userId))).CheckErrors(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
using (DataFilter.Disable<ISoftDelete>()) |
|||
{ |
|||
var deletedUser = await IdentityUserRepository.FindAsync(userId); |
|||
deletedUser.IsDeleted.ShouldBeTrue(); |
|||
deletedUser.PasswordHistories.Count.ShouldBe(0); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public virtual async Task Should_Remove_The_Related_Data_Of_A_Tenant_User_When_The_Unit_Of_Work_Completes_In_The_Host() |
|||
{ |
|||
var tenantId = Guid.NewGuid(); |
|||
var userId = Guid.NewGuid(); |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
await IdentityUserRepository.InsertAsync( |
|||
new IdentityUser(userId, $"tenant-{userId:N}", $"tenant-{userId:N}@abp.io", tenantId)); |
|||
await IdentitySessionRepository.InsertAsync(new IdentitySession( |
|||
Guid.NewGuid(), $"tenant-session-{userId:N}", "Web", "Chrome", userId, tenantId, "MyApp", "127.0.0.1", DateTime.UtcNow)); |
|||
await IdentityUserDelegationRepository.InsertAsync(new IdentityUserDelegation( |
|||
Guid.NewGuid(), userId, Guid.NewGuid(), DateTime.UtcNow, DateTime.UtcNow.AddDays(1), tenantId)); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
//The event is published while the unit of work completes, the current tenant is the host then.
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
await IdentityUserRepository.DeleteAsync(await IdentityUserRepository.GetAsync(userId)); |
|||
} |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
(await IdentitySessionRepository.GetCountAsync(userId: userId)).ShouldBe(0); |
|||
(await IdentityUserDelegationRepository.GetListAsync(sourceUserId: userId, targetUserId: null)).ShouldBeEmpty(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public virtual async Task Should_Remove_The_Dynamic_Claims_Cache_Of_A_Deleted_User() |
|||
{ |
|||
var userId = Guid.NewGuid(); |
|||
var cacheKey = AbpDynamicClaimCacheItem.CalculateCacheKey(userId, null); |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
(await IdentityUserManager.CreateAsync( |
|||
new IdentityUser(userId, $"claims-cache-{userId:N}", $"claims-cache-{userId:N}@abp.io"))).CheckErrors(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
await DynamicClaimCache.SetAsync(cacheKey, new AbpDynamicClaimCacheItem()); |
|||
(await DynamicClaimCache.GetAsync(cacheKey)).ShouldNotBeNull(); |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
(await IdentityUserManager.DeleteAsync(await IdentityUserRepository.GetAsync(userId))).CheckErrors(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
(await DynamicClaimCache.GetAsync(cacheKey)).ShouldBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public virtual async Task Deleting_A_Stale_User_Should_Throw_A_Concurrency_Exception() |
|||
{ |
|||
var userId = Guid.NewGuid(); |
|||
IdentityUser staleUser; |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
(await IdentityUserManager.CreateAsync( |
|||
new IdentityUser(userId, $"stale-{userId:N}", $"stale-{userId:N}@abp.io"))).CheckErrors(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
staleUser = await IdentityUserRepository.GetAsync(userId); |
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
//Change the user, so the instance loaded above has an old concurrency stamp.
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
var user = await IdentityUserRepository.GetAsync(userId); |
|||
user.Name = "Changed"; |
|||
await IdentityUserRepository.UpdateAsync(user); |
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
await Should.ThrowAsync<AbpIdentityResultException>( |
|||
async () => await IdentityUserManager.DeleteAsync(staleUser)); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public virtual async Task Deleting_A_User_Through_The_Repository_Should_Not_Clear_Its_Own_Collections() |
|||
{ |
|||
var userId = Guid.NewGuid(); |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
(await IdentityUserManager.CreateAsync( |
|||
new IdentityUser(userId, $"repo-collections-{userId:N}", $"repo-collections-{userId:N}@abp.io"))).CheckErrors(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
var user = await IdentityUserManager.GetByIdAsync(userId); |
|||
(await IdentityUserManager.AddToRoleAsync(user, "moderator")).CheckErrors(); |
|||
user.AddPasswordHistory("test"); |
|||
await IdentityUserRepository.UpdateAsync(user); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
await IdentityUserRepository.DeleteAsync(await IdentityUserRepository.GetAsync(userId)); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
//UserDeletedEventHandler only covers the aggregates that have no navigation from the user,
|
|||
//the collections of the user itself are cleared by IdentityUserManager.DeleteAsync.
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
using (DataFilter.Disable<ISoftDelete>()) |
|||
{ |
|||
var deletedUser = await IdentityUserRepository.FindAsync(userId); |
|||
deletedUser.Roles.Count.ShouldBe(1); |
|||
deletedUser.PasswordHistories.Count.ShouldBe(1); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public virtual async Task Deleting_A_Stale_User_Should_Not_Delete_Its_Link_Users() |
|||
{ |
|||
var userId = Guid.NewGuid(); |
|||
var linkedUserId = Guid.NewGuid(); |
|||
IdentityUser staleUser; |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
(await IdentityUserManager.CreateAsync( |
|||
new IdentityUser(userId, $"stale-link-{userId:N}", $"stale-link-{userId:N}@abp.io"))).CheckErrors(); |
|||
(await IdentityUserManager.CreateAsync( |
|||
new IdentityUser(linkedUserId, $"stale-linked-{linkedUserId:N}", $"stale-linked-{linkedUserId:N}@abp.io"))).CheckErrors(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
await IdentityLinkUserManager.LinkAsync( |
|||
new IdentityLinkUserInfo(userId), |
|||
new IdentityLinkUserInfo(linkedUserId)); |
|||
|
|||
staleUser = await IdentityUserRepository.GetAsync(userId); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
var user = await IdentityUserRepository.GetAsync(userId); |
|||
user.Name = "Changed"; |
|||
await IdentityUserRepository.UpdateAsync(user); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
await Should.ThrowAsync<AbpIdentityResultException>( |
|||
async () => await IdentityUserManager.DeleteAsync(staleUser)); |
|||
} |
|||
|
|||
//The user is still there, so its link users must be there as well.
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
(await IdentityLinkUserManager.IsLinkedAsync( |
|||
new IdentityLinkUserInfo(userId), |
|||
new IdentityLinkUserInfo(linkedUserId))).ShouldBeTrue(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Identity; |
|||
using Shouldly; |
|||
using Volo.Abp.Guids; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Uow; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Identity; |
|||
|
|||
public abstract class OrganizationUnitManager_CreateMany_Tests<TStartupModule> : AbpIdentityTestBase<TStartupModule> |
|||
where TStartupModule : IAbpModule |
|||
{ |
|||
protected OrganizationUnitManager OrganizationUnitManager { get; } |
|||
protected IOrganizationUnitRepository OrganizationUnitRepository { get; } |
|||
protected ILookupNormalizer LookupNormalizer { get; } |
|||
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
|||
protected IGuidGenerator GuidGenerator { get; } |
|||
|
|||
protected OrganizationUnitManager_CreateMany_Tests() |
|||
{ |
|||
OrganizationUnitManager = GetRequiredService<OrganizationUnitManager>(); |
|||
OrganizationUnitRepository = GetRequiredService<IOrganizationUnitRepository>(); |
|||
LookupNormalizer = GetRequiredService<ILookupNormalizer>(); |
|||
UnitOfWorkManager = GetRequiredService<IUnitOfWorkManager>(); |
|||
GuidGenerator = GetRequiredService<IGuidGenerator>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public virtual async Task Should_Not_Insert_Anything_When_A_Group_Is_Not_Valid() |
|||
{ |
|||
var validDisplayName = $"valid-{Guid.NewGuid():N}"; |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
var parent = await OrganizationUnitRepository.GetAsync("OU1"); |
|||
|
|||
//The second group is not valid, OU11 is already a child of OU1.
|
|||
await Should.ThrowAsync<BusinessException>(async () => |
|||
await OrganizationUnitManager.CreateManyAsync([ |
|||
new OrganizationUnit(GuidGenerator.Create(), validDisplayName), |
|||
new OrganizationUnit(GuidGenerator.Create(), "OU11", parent.Id) |
|||
])); |
|||
} |
|||
|
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
(await OrganizationUnitRepository.GetAsync(validDisplayName)).ShouldBeNull(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue