Browse Source

Address GitHub Copilot and Codex review on #25450

- SetLinkConsentAsync uses FindByIdAsync + null no-op, matching the
  missing-user behaviour of Get/RemoveLinkConsentAsync.
- Decorate Set/Get/RemoveLinkConsentAsync with [UnitOfWork] so the
  underlying IdentityUserStore.GetTokenAsync can EnsureCollectionLoaded
  the user.Tokens collection (fixes the CI failure on
  GetLinkConsentAsync_Should_Return_Null_When_No_Consent_Written).
- Clarify LinkUserTokenProvider XML doc to note that the single-active
  policy is enforced per purpose (matches AbpSingleActiveTokenProvider).
- Rename LinkUserTokenProvider_Should_Be_Register to *_Registered for
  consistency with sibling token-provider tests.
- Add tests covering: (a) RemoveLinkUserTokenAsync(purpose) only
  invalidates the requested purpose, and (b) same-purpose GenerateLink
  TokenAsync invalidates the previously issued token.
pull/25450/head
maliming 4 months ago
parent
commit
fdfb8e5db9
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 2
      modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/LinkUserTokenProvider.cs
  2. 10
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityLinkUserManager.cs
  3. 48
      modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/LinkUserTokenProvider_Tests.cs

2
modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/LinkUserTokenProvider.cs

@ -8,7 +8,7 @@ using Volo.Abp.Threading;
namespace Volo.Abp.Identity.AspNetCore;
/// <summary>
/// Link-user token provider that enforces only the most recently issued
/// Link-user token provider that enforces, per purpose, only the most recently issued
/// token to be valid, with a configurable expiration period.
/// </summary>
public class LinkUserTokenProvider : AbpSingleActiveTokenProvider

10
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityLinkUserManager.cs

@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Volo.Abp.Domain.Services;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace Volo.Abp.Identity;
@ -162,11 +163,16 @@ public class IdentityLinkUserManager : DomainService
}
}
[UnitOfWork]
public virtual async Task SetLinkConsentAsync(IdentityLinkUserInfo sourceLinkUser, string consent, CancellationToken cancellationToken = default)
{
using (CurrentTenant.Change(sourceLinkUser.TenantId))
{
var user = await UserManager.GetByIdAsync(sourceLinkUser.UserId);
var user = await UserManager.FindByIdAsync(sourceLinkUser.UserId.ToString());
if (user == null)
{
return;
}
(await UserManager.SetAuthenticationTokenAsync(
user,
LinkUserTokenProviderConsts.LinkUserConsentLoginProvider,
@ -175,6 +181,7 @@ public class IdentityLinkUserManager : DomainService
}
}
[UnitOfWork]
public virtual async Task<string?> GetLinkConsentAsync(IdentityLinkUserInfo sourceLinkUser, CancellationToken cancellationToken = default)
{
using (CurrentTenant.Change(sourceLinkUser.TenantId))
@ -191,6 +198,7 @@ public class IdentityLinkUserManager : DomainService
}
}
[UnitOfWork]
public virtual async Task RemoveLinkConsentAsync(IdentityLinkUserInfo sourceLinkUser, CancellationToken cancellationToken = default)
{
using (CurrentTenant.Change(sourceLinkUser.TenantId))

48
modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/LinkUserTokenProvider_Tests.cs

@ -34,7 +34,7 @@ public class LinkUserTokenProvider_Tests : AbpSingleActiveTokenProviderTestBase
=> LinkUserTokenProviderConsts.LinkUserTokenPurpose;
[Fact]
public void LinkUserTokenProvider_Should_Be_Register()
public void LinkUserTokenProvider_Should_Be_Registered()
{
var identityOptions = GetRequiredService<IOptions<IdentityOptions>>().Value;
@ -76,4 +76,50 @@ public class LinkUserTokenProvider_Tests : AbpSingleActiveTokenProviderTestBase
await uow.CompleteAsync();
}
}
[Fact]
public async Task RemoveLinkUserTokenAsync_With_Custom_Purpose_Should_Invalidate_Only_That_Purpose()
{
const string customPurpose = "CustomLinkUserPurpose";
using (var uow = UnitOfWorkManager.Begin())
{
var john = await UserRepository.GetAsync(TestData.UserJohnId);
var defaultPurposeToken = await GenerateTokenAsync(john);
var customPurposeToken = await IdentityLinkUserManager.GenerateLinkTokenAsync(
new IdentityLinkUserInfo(john.Id, john.TenantId),
customPurpose);
john = await UserRepository.GetAsync(TestData.UserJohnId);
(await UserManager.RemoveLinkUserTokenAsync(john, customPurpose)).Succeeded.ShouldBeTrue();
john = await UserRepository.GetAsync(TestData.UserJohnId);
(await IdentityLinkUserManager.VerifyLinkTokenAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), customPurposeToken, customPurpose)).ShouldBeFalse();
(await VerifyTokenAsync(john, defaultPurposeToken)).ShouldBeTrue();
await uow.CompleteAsync();
}
}
[Fact]
public async Task GenerateLinkTokenAsync_With_Custom_Purpose_Should_Invalidate_Previous_Token_For_Same_Purpose()
{
const string customPurpose = "CustomLinkUserPurpose";
using (var uow = UnitOfWorkManager.Begin())
{
var john = await UserRepository.GetAsync(TestData.UserJohnId);
var firstToken = await IdentityLinkUserManager.GenerateLinkTokenAsync(
new IdentityLinkUserInfo(john.Id, john.TenantId),
customPurpose);
var secondToken = await IdentityLinkUserManager.GenerateLinkTokenAsync(
new IdentityLinkUserInfo(john.Id, john.TenantId),
customPurpose);
(await IdentityLinkUserManager.VerifyLinkTokenAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), firstToken, customPurpose)).ShouldBeFalse();
(await IdentityLinkUserManager.VerifyLinkTokenAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), secondToken, customPurpose)).ShouldBeTrue();
await uow.CompleteAsync();
}
}
}

Loading…
Cancel
Save