Files
tiku-backend.net/Tiku.Infrastructure/Security/AuthorizationCacheInvalidationProcessor.cs

62 lines
2.6 KiB
C#

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;
}
}