mirror of https://github.com/abpframework/abp.git
committed by
GitHub
42 changed files with 928 additions and 104 deletions
@ -1,7 +1,9 @@ |
|||
namespace Volo.Abp.BackgroundJobs |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.BackgroundJobs |
|||
{ |
|||
public interface IBackgroundJobExecuter |
|||
{ |
|||
void Execute(JobExecutionContext context); |
|||
Task ExecuteAsync(JobExecutionContext context); |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.BackgroundWorkers |
|||
{ |
|||
public abstract class AsyncPeriodicBackgroundWorkerBase : BackgroundWorkerBase |
|||
{ |
|||
protected IServiceScopeFactory ServiceScopeFactory { get; } |
|||
protected AbpTimer Timer { get; } |
|||
|
|||
protected AsyncPeriodicBackgroundWorkerBase( |
|||
AbpTimer timer, |
|||
IServiceScopeFactory serviceScopeFactory) |
|||
{ |
|||
ServiceScopeFactory = serviceScopeFactory; |
|||
Timer = timer; |
|||
Timer.Elapsed += Timer_Elapsed; |
|||
} |
|||
|
|||
public override async Task StartAsync(CancellationToken cancellationToken = default) |
|||
{ |
|||
await base.StartAsync(cancellationToken).ConfigureAwait(false); |
|||
Timer.Start(cancellationToken); |
|||
} |
|||
|
|||
public override async Task StopAsync(CancellationToken cancellationToken = default) |
|||
{ |
|||
Timer.Stop(cancellationToken); |
|||
await base.StopAsync(cancellationToken).ConfigureAwait(false); |
|||
} |
|||
|
|||
private void Timer_Elapsed(object sender, System.EventArgs e) |
|||
{ |
|||
try |
|||
{ |
|||
using (var scope = ServiceScopeFactory.CreateScope()) |
|||
{ |
|||
AsyncHelper.RunSync( |
|||
() => DoWorkAsync(new PeriodicBackgroundWorkerContext(scope.ServiceProvider)) |
|||
); |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogException(ex); |
|||
} |
|||
} |
|||
|
|||
protected abstract Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext); |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.BackgroundWorkers |
|||
{ |
|||
public class PeriodicBackgroundWorkerContext |
|||
{ |
|||
public IServiceProvider ServiceProvider { get; } |
|||
|
|||
public PeriodicBackgroundWorkerContext(IServiceProvider serviceProvider) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
using System; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
|
|||
namespace Volo.Abp.IdentityServer.Devices |
|||
{ |
|||
public class DeviceFlowCodes : CreationAuditedAggregateRoot<Guid> |
|||
{ |
|||
public virtual string DeviceCode { get; set; } |
|||
|
|||
public virtual string UserCode { get; set; } |
|||
|
|||
public virtual string SubjectId { get; set; } |
|||
|
|||
public virtual string ClientId { get; set; } |
|||
|
|||
public virtual DateTime? Expiration { get; set; } |
|||
|
|||
public virtual string Data { get; set; } |
|||
|
|||
private DeviceFlowCodes() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public DeviceFlowCodes(Guid id) |
|||
: base(id) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,143 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using IdentityModel; |
|||
using IdentityServer4.Models; |
|||
using IdentityServer4.Stores; |
|||
using IdentityServer4.Stores.Serialization; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Guids; |
|||
|
|||
namespace Volo.Abp.IdentityServer.Devices |
|||
{ |
|||
public class DeviceFlowStore : IDeviceFlowStore, ITransientDependency |
|||
{ |
|||
protected IDeviceFlowCodesRepository DeviceFlowCodesRepository { get; } |
|||
protected IGuidGenerator GuidGenerator { get; } |
|||
protected IPersistentGrantSerializer PersistentGrantSerializer { get; } |
|||
|
|||
public DeviceFlowStore( |
|||
IDeviceFlowCodesRepository deviceFlowCodesRepository, |
|||
IGuidGenerator guidGenerator, |
|||
IPersistentGrantSerializer persistentGrantSerializer) |
|||
{ |
|||
DeviceFlowCodesRepository = deviceFlowCodesRepository; |
|||
GuidGenerator = guidGenerator; |
|||
PersistentGrantSerializer = persistentGrantSerializer; |
|||
} |
|||
|
|||
public async Task StoreDeviceAuthorizationAsync(string deviceCode, string userCode, DeviceCode data) |
|||
{ |
|||
Check.NotNull(deviceCode, nameof(deviceCode)); |
|||
Check.NotNull(userCode, nameof(userCode)); |
|||
Check.NotNull(data, nameof(data)); |
|||
|
|||
await DeviceFlowCodesRepository |
|||
.InsertAsync( |
|||
new DeviceFlowCodes(GuidGenerator.Create()) |
|||
{ |
|||
DeviceCode = deviceCode, |
|||
UserCode = userCode, |
|||
ClientId = data.ClientId, |
|||
SubjectId = data.Subject?.FindFirst(JwtClaimTypes.Subject).Value, |
|||
CreationTime = data.CreationTime, |
|||
Expiration = data.CreationTime.AddSeconds(data.Lifetime), |
|||
Data = Serialize(data) |
|||
} |
|||
).ConfigureAwait(false); |
|||
} |
|||
|
|||
public async Task<DeviceCode> FindByUserCodeAsync(string userCode) |
|||
{ |
|||
Check.NotNull(userCode, nameof(userCode)); |
|||
|
|||
var deviceCodes = await DeviceFlowCodesRepository |
|||
.FindByUserCodeAsync(userCode) |
|||
.ConfigureAwait(false); |
|||
|
|||
if (deviceCodes == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return DeserializeToDeviceCode(deviceCodes.Data); |
|||
} |
|||
|
|||
public async Task<DeviceCode> FindByDeviceCodeAsync(string deviceCode) |
|||
{ |
|||
Check.NotNull(deviceCode, nameof(deviceCode)); |
|||
|
|||
var deviceCodes = await DeviceFlowCodesRepository |
|||
.FindByDeviceCodeAsync(deviceCode) |
|||
.ConfigureAwait(false); |
|||
|
|||
if (deviceCodes == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return DeserializeToDeviceCode(deviceCodes.Data); |
|||
} |
|||
|
|||
public async Task UpdateByUserCodeAsync(string userCode, DeviceCode data) |
|||
{ |
|||
Check.NotNull(userCode, nameof(userCode)); |
|||
Check.NotNull(data, nameof(data)); |
|||
|
|||
|
|||
var deviceCodes = await DeviceFlowCodesRepository |
|||
.FindByUserCodeAsync(userCode) |
|||
.ConfigureAwait(false); |
|||
|
|||
if (deviceCodes == null) |
|||
{ |
|||
throw new InvalidOperationException($"Could not update device code by the given userCode: {userCode}"); |
|||
} |
|||
|
|||
deviceCodes.SubjectId = data.Subject?.FindFirst(JwtClaimTypes.Subject).Value; |
|||
deviceCodes.Data = Serialize(data); |
|||
|
|||
await DeviceFlowCodesRepository |
|||
.UpdateAsync(deviceCodes, autoSave: true) |
|||
.ConfigureAwait(false); |
|||
} |
|||
|
|||
public async Task RemoveByDeviceCodeAsync(string deviceCode) |
|||
{ |
|||
Check.NotNull(deviceCode, nameof(deviceCode)); |
|||
|
|||
var deviceCodes = await DeviceFlowCodesRepository |
|||
.FindByDeviceCodeAsync(deviceCode) |
|||
.ConfigureAwait(false); |
|||
|
|||
if (deviceCodes == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
await DeviceFlowCodesRepository |
|||
.DeleteAsync(deviceCodes, autoSave: true) |
|||
.ConfigureAwait(false); |
|||
} |
|||
|
|||
private string Serialize([CanBeNull] DeviceCode deviceCode) |
|||
{ |
|||
if (deviceCode == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return PersistentGrantSerializer.Serialize(deviceCode); |
|||
} |
|||
|
|||
protected virtual DeviceCode DeserializeToDeviceCode([CanBeNull] string data) |
|||
{ |
|||
if (data == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return PersistentGrantSerializer.Deserialize<DeviceCode>(data); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace Volo.Abp.IdentityServer.Devices |
|||
{ |
|||
public interface IDeviceFlowCodesRepository : IBasicRepository<DeviceFlowCodes, Guid> |
|||
{ |
|||
Task<DeviceFlowCodes> FindByUserCodeAsync( |
|||
string userCode, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task<DeviceFlowCodes> FindByDeviceCodeAsync( |
|||
string deviceCode, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
Task<List<DeviceFlowCodes>> GetListByExpirationAsync( |
|||
DateTime maxExpirationDate, |
|||
int maxResultCount, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.BackgroundWorkers; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.IdentityServer.Tokens |
|||
{ |
|||
public class TokenCleanupBackgroundWorker : AsyncPeriodicBackgroundWorkerBase |
|||
{ |
|||
protected TokenCleanupOptions Options { get; } |
|||
|
|||
public TokenCleanupBackgroundWorker( |
|||
AbpTimer timer, |
|||
IServiceScopeFactory serviceScopeFactory, |
|||
IOptions<TokenCleanupOptions> options) |
|||
: base( |
|||
timer, |
|||
serviceScopeFactory) |
|||
{ |
|||
Options = options.Value; |
|||
timer.Period = Options.CleanupPeriod; |
|||
} |
|||
|
|||
protected override async Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext) |
|||
{ |
|||
await workerContext |
|||
.ServiceProvider |
|||
.GetRequiredService<TokenCleanupService>() |
|||
.CleanAsync() |
|||
.ConfigureAwait(false); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using Volo.Abp.BackgroundWorkers; |
|||
|
|||
namespace Volo.Abp.IdentityServer.Tokens |
|||
{ |
|||
public class TokenCleanupOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Default: 3,600,000 ms.
|
|||
/// </summary>
|
|||
public int CleanupPeriod { get; set; } = 3_600_000; |
|||
|
|||
/// <summary>
|
|||
/// Default value: 100.
|
|||
/// </summary>
|
|||
public int CleanupBatchSize { get; set; } = 100; |
|||
|
|||
/// <summary>
|
|||
/// The number of loop if there are
|
|||
/// more than <see cref="CleanupBatchSize"/> tokens in the database.
|
|||
/// So, if <see cref="CleanupLoopCount"/> is 10 and <see cref="CleanupBatchSize"/> is 100,
|
|||
/// then the cleanup worker will clean 1,000 items in one <see cref="CleanupPeriod"/> at max.
|
|||
///
|
|||
/// Default value: 10.
|
|||
/// </summary>
|
|||
public int CleanupLoopCount { get; set; } = 10; |
|||
|
|||
/// <summary>
|
|||
/// Default value: true.
|
|||
/// If <see cref="AbpBackgroundWorkerOptions.IsEnabled"/> is false,
|
|||
/// this property is ignored and the cleanup worker doesn't work for this application instance.
|
|||
/// </summary>
|
|||
public bool IsCleanupEnabled { get; set; } = true; |
|||
} |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.IdentityServer.Devices; |
|||
using Volo.Abp.IdentityServer.Grants; |
|||
using Volo.Abp.Timing; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace Volo.Abp.IdentityServer.Tokens |
|||
{ |
|||
public class TokenCleanupService : ITransientDependency |
|||
{ |
|||
protected IPersistentGrantRepository PersistentGrantRepository { get; } |
|||
protected IDeviceFlowCodesRepository DeviceFlowCodesRepository { get; } |
|||
protected IClock Clock { get; } |
|||
protected TokenCleanupOptions Options { get; } |
|||
|
|||
public TokenCleanupService( |
|||
IPersistentGrantRepository persistentGrantRepository, |
|||
IDeviceFlowCodesRepository deviceFlowCodesRepository, |
|||
IClock clock, |
|||
IOptions<TokenCleanupOptions> options) |
|||
{ |
|||
PersistentGrantRepository = persistentGrantRepository; |
|||
DeviceFlowCodesRepository = deviceFlowCodesRepository; |
|||
Clock = clock; |
|||
Options = options.Value; |
|||
} |
|||
|
|||
public virtual async Task CleanAsync() |
|||
{ |
|||
await RemoveGrantsAsync() |
|||
.ConfigureAwait(false); |
|||
|
|||
await RemoveDeviceCodesAsync() |
|||
.ConfigureAwait(false); |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
protected virtual async Task RemoveGrantsAsync() |
|||
{ |
|||
for (int i = 0; i < Options.CleanupLoopCount; i++) |
|||
{ |
|||
var persistentGrants = await PersistentGrantRepository |
|||
.GetListByExpirationAsync(Clock.Now, Options.CleanupBatchSize) |
|||
.ConfigureAwait(false); |
|||
|
|||
//TODO: Can be optimized if the repository implements the batch deletion
|
|||
foreach (var persistentGrant in persistentGrants) |
|||
{ |
|||
await PersistentGrantRepository |
|||
.DeleteAsync(persistentGrant) |
|||
.ConfigureAwait(false); |
|||
} |
|||
|
|||
//No need to continue to query if it gets more than max items.
|
|||
if (persistentGrants.Count < Options.CleanupBatchSize) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task RemoveDeviceCodesAsync() |
|||
{ |
|||
for (int i = 0; i < Options.CleanupLoopCount; i++) |
|||
{ |
|||
var deviceFlowCodeses = await DeviceFlowCodesRepository |
|||
.GetListByExpirationAsync(Clock.Now, Options.CleanupBatchSize) |
|||
.ConfigureAwait(false); |
|||
|
|||
//TODO: Can be optimized if the repository implements the batch deletion
|
|||
foreach (var deviceFlowCodes in deviceFlowCodeses) |
|||
{ |
|||
await DeviceFlowCodesRepository |
|||
.DeleteAsync(deviceFlowCodes) |
|||
.ConfigureAwait(false); |
|||
} |
|||
|
|||
//No need to continue to query if it gets more than max items.
|
|||
if (deviceFlowCodeses.Count < Options.CleanupBatchSize) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
using Volo.Abp.IdentityServer.EntityFrameworkCore; |
|||
|
|||
namespace Volo.Abp.IdentityServer.Devices |
|||
{ |
|||
public class DeviceFlowCodesRepository : EfCoreRepository<IIdentityServerDbContext, DeviceFlowCodes, Guid>, |
|||
IDeviceFlowCodesRepository |
|||
{ |
|||
public DeviceFlowCodesRepository(IDbContextProvider<IIdentityServerDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public async Task<DeviceFlowCodes> FindByUserCodeAsync( |
|||
string userCode, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet |
|||
.FirstOrDefaultAsync(d => d.UserCode == userCode, GetCancellationToken(cancellationToken)) |
|||
.ConfigureAwait(false); |
|||
} |
|||
|
|||
public async Task<DeviceFlowCodes> FindByDeviceCodeAsync( |
|||
string deviceCode, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet |
|||
.FirstOrDefaultAsync(d => d.DeviceCode == deviceCode, GetCancellationToken(cancellationToken)) |
|||
.ConfigureAwait(false); |
|||
} |
|||
|
|||
public async Task<List<DeviceFlowCodes>> GetListByExpirationAsync(DateTime maxExpirationDate, int maxResultCount, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet |
|||
.Where(x => x.Expiration != null && x.Expiration < maxExpirationDate) |
|||
.OrderBy(x => x.ClientId) |
|||
.Take(maxResultCount) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)) |
|||
.ConfigureAwait(false); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using MongoDB.Driver; |
|||
using MongoDB.Driver.Linq; |
|||
using Volo.Abp.Domain.Repositories.MongoDB; |
|||
using Volo.Abp.IdentityServer.Devices; |
|||
using Volo.Abp.MongoDB; |
|||
|
|||
namespace Volo.Abp.IdentityServer.MongoDB |
|||
{ |
|||
public class MongoDeviceFlowCodesRepository : |
|||
MongoDbRepository<IAbpIdentityServerMongoDbContext, DeviceFlowCodes, Guid>, IDeviceFlowCodesRepository |
|||
{ |
|||
public MongoDeviceFlowCodesRepository( |
|||
IMongoDbContextProvider<IAbpIdentityServerMongoDbContext> dbContextProvider) : base(dbContextProvider) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public async Task<DeviceFlowCodes> FindByUserCodeAsync( |
|||
string userCode, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await GetMongoQueryable() |
|||
.FirstOrDefaultAsync(d => d.UserCode == userCode, GetCancellationToken(cancellationToken)) |
|||
.ConfigureAwait(false); |
|||
} |
|||
|
|||
public async Task<DeviceFlowCodes> FindByDeviceCodeAsync(string deviceCode, CancellationToken cancellationToken = default) |
|||
{ |
|||
return await GetMongoQueryable() |
|||
.FirstOrDefaultAsync(d => d.DeviceCode == deviceCode, GetCancellationToken(cancellationToken)) |
|||
.ConfigureAwait(false); |
|||
} |
|||
|
|||
public async Task<List<DeviceFlowCodes>> GetListByExpirationAsync( |
|||
DateTime maxExpirationDate, |
|||
int maxResultCount, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return await GetMongoQueryable() |
|||
.Where(x => x.Expiration != null && x.Expiration < maxExpirationDate) |
|||
.OrderBy(x => x.ClientId) |
|||
.Take(maxResultCount) |
|||
.ToListAsync(GetCancellationToken(cancellationToken)) |
|||
.ConfigureAwait(false); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,95 @@ |
|||
using System.Linq; |
|||
using System.Security.Claims; |
|||
using System.Threading.Tasks; |
|||
using IdentityModel; |
|||
using IdentityServer4.Models; |
|||
using IdentityServer4.Stores; |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.IdentityServer.Devices |
|||
{ |
|||
public class DeviceFlowStore_Tests : AbpIdentityServerTestBase |
|||
{ |
|||
private readonly IDeviceFlowStore _deviceFlowStore; |
|||
|
|||
public DeviceFlowStore_Tests() |
|||
{ |
|||
_deviceFlowStore = GetRequiredService<IDeviceFlowStore>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task StoreDeviceAuthorizationAsync() |
|||
{ |
|||
await _deviceFlowStore.StoreDeviceAuthorizationAsync( |
|||
"DeviceCode-Test1", |
|||
"UserCode-Test1", |
|||
new DeviceCode |
|||
{ |
|||
ClientId = "ClientId1", |
|||
AuthorizedScopes = new string[] { "s1", "s2" }, |
|||
IsAuthorized = true, |
|||
Lifetime = 42, |
|||
RequestedScopes = new string[] { "rs1" }, |
|||
Subject = new ClaimsPrincipal( |
|||
new[] |
|||
{ |
|||
new ClaimsIdentity(new[] |
|||
{ |
|||
new Claim(JwtClaimTypes.Subject, "sid1") |
|||
}) |
|||
} |
|||
) |
|||
} |
|||
); |
|||
|
|||
void Check(DeviceCode deviceCode) |
|||
{ |
|||
deviceCode.ClientId.ShouldBe("ClientId1"); |
|||
deviceCode.AuthorizedScopes.ShouldNotBeNull(); |
|||
deviceCode.AuthorizedScopes.Count().ShouldBe(2); |
|||
deviceCode.AuthorizedScopes.ShouldContain("s1"); |
|||
deviceCode.AuthorizedScopes.ShouldContain("s2"); |
|||
deviceCode.IsAuthorized.ShouldBeTrue(); |
|||
deviceCode.Lifetime.ShouldBe(42); |
|||
deviceCode.RequestedScopes.ShouldNotBeNull(); |
|||
deviceCode.RequestedScopes.Count().ShouldBe(1); |
|||
deviceCode.RequestedScopes.ShouldContain("rs1"); |
|||
deviceCode.Subject.ShouldNotBeNull(); |
|||
deviceCode.Subject.Claims.ShouldContain(x => x.Type == JwtClaimTypes.Subject && x.Value == "sid1"); |
|||
} |
|||
|
|||
Check(await _deviceFlowStore.FindByUserCodeAsync("UserCode-Test1")); |
|||
Check(await _deviceFlowStore.FindByDeviceCodeAsync("DeviceCode-Test1")); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveByDeviceCodeAsync() |
|||
{ |
|||
(await _deviceFlowStore.FindByDeviceCodeAsync("DeviceCode1")).ShouldNotBeNull(); |
|||
|
|||
await _deviceFlowStore.RemoveByDeviceCodeAsync("DeviceCode1"); |
|||
|
|||
(await _deviceFlowStore.FindByDeviceCodeAsync("DeviceCode1")).ShouldBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task UpdateByDeviceCodeAsync() |
|||
{ |
|||
var deviceCode = await _deviceFlowStore.FindByDeviceCodeAsync("DeviceCode1"); |
|||
deviceCode.ShouldNotBeNull(); |
|||
deviceCode.Lifetime.ShouldBe(42); |
|||
|
|||
await _deviceFlowStore.UpdateByUserCodeAsync( |
|||
"DeviceFlowCodesUserCode1", |
|||
new DeviceCode |
|||
{ |
|||
Lifetime = 43 |
|||
} |
|||
); |
|||
|
|||
deviceCode = await _deviceFlowStore.FindByDeviceCodeAsync("DeviceCode1"); |
|||
deviceCode.Lifetime.ShouldBe(43); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using System.Threading.Tasks; |
|||
using Shouldly; |
|||
using Volo.Abp.IdentityServer.Devices; |
|||
using Volo.Abp.IdentityServer.Grants; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.IdentityServer.Tokens |
|||
{ |
|||
public class TokenCleanupService_Tests : AbpIdentityServerTestBase |
|||
{ |
|||
private readonly IPersistentGrantRepository _persistentGrantRepository; |
|||
private readonly IDeviceFlowCodesRepository _deviceFlowCodesRepository; |
|||
private readonly TokenCleanupService _tokenCleanupService; |
|||
|
|||
public TokenCleanupService_Tests() |
|||
{ |
|||
_persistentGrantRepository = GetRequiredService<IPersistentGrantRepository>(); |
|||
_deviceFlowCodesRepository = GetRequiredService<IDeviceFlowCodesRepository>(); |
|||
_tokenCleanupService = GetRequiredService<TokenCleanupService>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Clear_Expired_Tokens() |
|||
{ |
|||
var persistentGrantCount = await _persistentGrantRepository.GetCountAsync(); |
|||
var deviceFlowCodesCount = await _deviceFlowCodesRepository.GetCountAsync(); |
|||
|
|||
await _tokenCleanupService.CleanAsync(); |
|||
|
|||
(await _persistentGrantRepository.GetCountAsync()) |
|||
.ShouldBe(persistentGrantCount - 1); |
|||
|
|||
(await _deviceFlowCodesRepository.GetCountAsync()) |
|||
.ShouldBe(deviceFlowCodesCount - 1); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue