Browse Source

Support shared mode lookup by id for two-factor authentication user

- Add IdentityUserManager.FindSharedUserByIdAsync to resolve a user by id across tenants in shared user sharing strategy
- Override AbpSignInManager.GetTwoFactorAuthenticationUserAsync to use it so the 2FA mid-flow can still find a tenant-scoped user when CurrentTenant is host
- Cover the new method with unit tests
pull/25304/head
maliming 2 weeks ago
parent
commit
fa9e4f13cf
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 20
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs
  2. 27
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs
  3. 60
      modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs

20
modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/AbpSignInManager.cs

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
@ -133,6 +134,23 @@ public class AbpSignInManager : SignInManager<IdentityUser>
return await IdentityUserManager.FindSharedUserByLoginAsync(loginProvider, providerKey);
}
public override async Task<IdentityUser> GetTwoFactorAuthenticationUserAsync()
{
var result = await Context.AuthenticateAsync(IdentityConstants.TwoFactorUserIdScheme);
if (result?.Principal == null)
{
return null;
}
var userId = result.Principal.FindFirstValue(ClaimTypes.Name);
if (string.IsNullOrWhiteSpace(userId))
{
return null;
}
return await IdentityUserManager.FindSharedUserByIdAsync(userId);
}
/// <summary>
/// This is to call the protection method PreSignInCheck
/// </summary>

27
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs

@ -718,4 +718,31 @@ public class IdentityUserManager : UserManager<IdentityUser>, IDomainService
}
}
public virtual async Task<IdentityUser> FindSharedUserByIdAsync(string userId)
{
if (MultiTenancyOptions.Value.UserSharingStrategy == TenantUserSharingStrategy.Isolated)
{
return await base.FindByIdAsync(userId);
}
using (CurrentTenant.Change(null))
{
using (DataFilter.Disable<IMultiTenant>())
{
var user = await base.FindByIdAsync(userId);
if (user == null)
{
return null;
}
using (DataFilter.Enable<IMultiTenant>())
{
using (CurrentTenant.Change(user.TenantId))
{
return await base.FindByIdAsync(userId);
}
}
}
}
}
}

60
modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs

@ -618,6 +618,66 @@ public class SharedTenantUserSharingStrategy_IdentityUserManager_Tests : AbpIden
}
}
[Fact]
public async Task FindSharedUserByIdAsync_Should_Find_Tenant_User_From_Host_Context()
{
var tenantId = Guid.NewGuid();
IdentityUser tenantUser;
using (var uow = _unitOfWorkManager.Begin())
{
tenantUser = await CreateUserAsync(tenantId, "shared-id-tenant-only", "shared-id-tenant-only@abp.io");
await uow.CompleteAsync();
}
// Simulates the 2FA mid-flow on a Shared deployment: CurrentTenant is null
// but the user row only exists under a tenant. FindByIdAsync alone would be
// filtered out by the IMultiTenant filter, so FindSharedUserByIdAsync must
// disable the filter and still return the tenant user.
using (_currentTenant.Change(null))
{
var user = await _identityUserManager.FindSharedUserByIdAsync(tenantUser.Id.ToString());
user.ShouldNotBeNull();
user.Id.ShouldBe(tenantUser.Id);
user.TenantId.ShouldBe(tenantId);
user.UserName.ShouldBe("shared-id-tenant-only");
}
}
[Fact]
public async Task FindSharedUserByIdAsync_Should_Find_Host_User_From_Tenant_Context()
{
var tenantId = Guid.NewGuid();
IdentityUser hostUser;
using (var uow = _unitOfWorkManager.Begin())
{
hostUser = await CreateUserAsync(null, "shared-id-host-only", "shared-id-host-only@abp.io");
await uow.CompleteAsync();
}
using (_currentTenant.Change(tenantId))
{
var user = await _identityUserManager.FindSharedUserByIdAsync(hostUser.Id.ToString());
user.ShouldNotBeNull();
user.Id.ShouldBe(hostUser.Id);
user.TenantId.ShouldBeNull();
user.UserName.ShouldBe("shared-id-host-only");
}
}
[Fact]
public async Task FindSharedUserByIdAsync_Should_Return_Null_For_Unknown_Id()
{
using (_currentTenant.Change(null))
{
var user = await _identityUserManager.FindSharedUserByIdAsync(Guid.NewGuid().ToString());
user.ShouldBeNull();
}
}
private async Task<IdentityUser> CreateUserAsync(
Guid? tenantId,
string userName,

Loading…
Cancel
Save