mirror of https://github.com/abpframework/abp.git
5 changed files with 200 additions and 374 deletions
@ -0,0 +1,138 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Identity; |
|||
using Shouldly; |
|||
using Volo.Abp.Uow; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Identity.AspNetCore; |
|||
|
|||
/// <summary>
|
|||
/// Abstract base class that exercises the common behaviour of every
|
|||
/// <see cref="AbpSingleActiveTokenProvider"/> subclass.
|
|||
/// Concrete subclasses inject their provider-specific generate/verify helpers
|
|||
/// so the same test suite runs against each provider.
|
|||
/// </summary>
|
|||
public abstract class AbpSingleActiveTokenProviderTestBase : AbpIdentityAspNetCoreTestBase |
|||
{ |
|||
protected IIdentityUserRepository UserRepository { get; } |
|||
protected IdentityUserManager UserManager { get; } |
|||
protected IdentityTestData TestData { get; } |
|||
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
|||
|
|||
protected AbpSingleActiveTokenProviderTestBase() |
|||
{ |
|||
UserRepository = GetRequiredService<IIdentityUserRepository>(); |
|||
UserManager = GetRequiredService<IdentityUserManager>(); |
|||
TestData = GetRequiredService<IdentityTestData>(); |
|||
UnitOfWorkManager = GetRequiredService<IUnitOfWorkManager>(); |
|||
} |
|||
|
|||
/// <summary>Generates a token for <paramref name="user"/> via the provider under test.</summary>
|
|||
protected abstract Task<string> GenerateTokenAsync(IdentityUser user); |
|||
|
|||
/// <summary>Verifies <paramref name="token"/> for <paramref name="user"/> via the provider under test.</summary>
|
|||
protected abstract Task<bool> VerifyTokenAsync(IdentityUser user, string token); |
|||
|
|||
/// <summary>Returns the provider name used to look up the stored hash.</summary>
|
|||
protected abstract string GetProviderName(); |
|||
|
|||
/// <summary>Returns the token purpose used as the hash key prefix.</summary>
|
|||
protected abstract string GetPurpose(); |
|||
|
|||
private string GetHashKey() => GetPurpose() + AbpSingleActiveTokenProvider.TokenHashSuffix; |
|||
|
|||
[Fact] |
|||
public async Task Generate_And_Verify_Token_Should_Succeed() |
|||
{ |
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
var user = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
|
|||
var token = await GenerateTokenAsync(user); |
|||
token.ShouldNotBeNullOrEmpty(); |
|||
|
|||
user = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
(await VerifyTokenAsync(user, token)).ShouldBeTrue(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Invalid_Token_Should_Fail_Verification() |
|||
{ |
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
var user = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
await GenerateTokenAsync(user); |
|||
|
|||
user = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
(await VerifyTokenAsync(user, "invalid-token-value")).ShouldBeFalse(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Second_Token_Should_Invalidate_First_Token() |
|||
{ |
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
var user = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
var firstToken = await GenerateTokenAsync(user); |
|||
|
|||
user = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
var secondToken = await GenerateTokenAsync(user); |
|||
|
|||
user = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
|
|||
(await VerifyTokenAsync(user, firstToken)).ShouldBeFalse(); |
|||
(await VerifyTokenAsync(user, secondToken)).ShouldBeTrue(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Corrupted_Hash_Should_Return_False_Instead_Of_Throwing() |
|||
{ |
|||
using (var uow = UnitOfWorkManager.Begin()) |
|||
{ |
|||
var user = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
var token = await GenerateTokenAsync(user); |
|||
|
|||
// Overwrite with a non-hex string to simulate data corruption.
|
|||
user = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
await UserManager.SetAuthenticationTokenAsync(user, GetProviderName(), GetHashKey(), "not-valid-hex!!!"); |
|||
|
|||
user = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
|
|||
// ValidateAsync must catch FormatException internally and return false.
|
|||
(await VerifyTokenAsync(user, token)).ShouldBeFalse(); |
|||
|
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Token_Hash_Should_Persist_Across_UnitOfWork_Boundaries() |
|||
{ |
|||
string token; |
|||
|
|||
// UoW 1: generate; UpdateAsync inside GenerateAsync must write the hash to the DB.
|
|||
using (var uow = UnitOfWorkManager.Begin(requiresNew: true)) |
|||
{ |
|||
var user = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
token = await GenerateTokenAsync(user); |
|||
await uow.CompleteAsync(); |
|||
} |
|||
|
|||
// UoW 2: validate with a fresh DbContext to confirm the hash was persisted.
|
|||
using (var uow = UnitOfWorkManager.Begin(requiresNew: true)) |
|||
{ |
|||
var user = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
(await VerifyTokenAsync(user, token)).ShouldBeTrue(); |
|||
await uow.CompleteAsync(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue