mirror of https://github.com/abpframework/abp.git
49 changed files with 966 additions and 131 deletions
@ -1,7 +1,9 @@ |
|||||
namespace Volo.Abp.BackgroundJobs |
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.BackgroundJobs |
||||
{ |
{ |
||||
public interface IBackgroundJobExecuter |
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