feat(auth): add Redis authorization caching
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Tiku.Application.Security;
|
||||
using Tiku.Infrastructure.Persistence;
|
||||
|
||||
namespace Tiku.Infrastructure.Security;
|
||||
|
||||
internal sealed class AuthorizationCacheInvalidationProcessor(
|
||||
TikuDbContext dbContext,
|
||||
IAccessSecurityCache cache) : IAuthorizationCacheInvalidationProcessor
|
||||
{
|
||||
public async Task<int> ProcessPendingAsync(int batchSize = 100, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!cache.IsConfigured)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var items = await dbContext.AuthorizationCacheInvalidations
|
||||
.Where(item => item.ProcessedAt == null)
|
||||
.OrderBy(item => item.CreatedAt)
|
||||
.Take(Math.Clamp(batchSize, 1, 500))
|
||||
.ToArrayAsync(cancellationToken);
|
||||
var processed = 0;
|
||||
foreach (var item in items)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (item.TargetType)
|
||||
{
|
||||
case "session" when item.SessionId.HasValue:
|
||||
await cache.InvalidateSessionAsync(item.SessionId.Value, cancellationToken);
|
||||
break;
|
||||
case "user" when item.UserId.HasValue:
|
||||
await cache.InvalidateUserAsync(item.UserId.Value, cancellationToken);
|
||||
break;
|
||||
case "tenant" when item.TenantId.HasValue:
|
||||
await cache.InvalidateTenantAsync(item.TenantId.Value, cancellationToken);
|
||||
break;
|
||||
case "membership" when item.TenantId.HasValue && item.UserId.HasValue:
|
||||
await cache.InvalidateMembershipAsync(item.TenantId.Value, item.UserId.Value, cancellationToken);
|
||||
break;
|
||||
case "scope" when item.Realm.HasValue && item.Version.HasValue:
|
||||
await cache.SetAuthorizationVersionAsync(item.Realm.Value, item.TenantId, item.Version.Value, cancellationToken);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid authorization cache invalidation {item.Id}.");
|
||||
}
|
||||
item.ProcessedAt = DateTimeOffset.UtcNow;
|
||||
item.LastError = null;
|
||||
item.AttemptCount++;
|
||||
processed++;
|
||||
}
|
||||
catch (Exception exception) when (exception is not OperationCanceledException)
|
||||
{
|
||||
item.AttemptCount++;
|
||||
item.LastError = exception.Message;
|
||||
}
|
||||
}
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
return processed;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user