Implemented hybrid cache for user sessions, api keys and database provided settings. Cleaned up startup and adjusted caching option models for features
This commit is contained in:
@@ -2,7 +2,7 @@ using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Caching.Hybrid;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moonlight.Api.Database;
|
||||
@@ -14,20 +14,20 @@ namespace Moonlight.Api.Implementations.ApiKeyScheme;
|
||||
public class ApiKeySchemeHandler : AuthenticationHandler<ApiKeySchemeOptions>
|
||||
{
|
||||
private readonly DatabaseRepository<ApiKey> ApiKeyRepository;
|
||||
private readonly IMemoryCache MemoryCache;
|
||||
private readonly HybridCache HybridCache;
|
||||
|
||||
private const string CacheKeyFormat = $"Moonlight.Api.{nameof(ApiKeySchemeHandler)}.{{0}}";
|
||||
public const string CacheKeyFormat = $"Moonlight.Api.{nameof(ApiKeySchemeHandler)}.{{0}}";
|
||||
|
||||
public ApiKeySchemeHandler(
|
||||
IOptionsMonitor<ApiKeySchemeOptions> options,
|
||||
ILoggerFactory logger,
|
||||
UrlEncoder encoder,
|
||||
DatabaseRepository<ApiKey> apiKeyRepository,
|
||||
IMemoryCache memoryCache
|
||||
HybridCache hybridCache
|
||||
) : base(options, logger, encoder)
|
||||
{
|
||||
ApiKeyRepository = apiKeyRepository;
|
||||
MemoryCache = memoryCache;
|
||||
HybridCache = hybridCache;
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
@@ -41,25 +41,30 @@ public class ApiKeySchemeHandler : AuthenticationHandler<ApiKeySchemeOptions>
|
||||
return AuthenticateResult.Fail("Invalid api key specified");
|
||||
|
||||
var cacheKey = string.Format(CacheKeyFormat, authHeaderValue);
|
||||
|
||||
if (!MemoryCache.TryGetValue<ApiKeySession>(cacheKey, out var apiKey))
|
||||
{
|
||||
apiKey = await ApiKeyRepository
|
||||
.Query()
|
||||
.Where(x => x.Key == authHeaderValue)
|
||||
.Select(x => new ApiKeySession(x.Permissions, x.ValidUntil))
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (apiKey == null)
|
||||
return AuthenticateResult.Fail("Invalid api key specified");
|
||||
var apiKey = await HybridCache.GetOrCreateAsync<ApiKeySession?>(
|
||||
cacheKey,
|
||||
async ct =>
|
||||
{
|
||||
var x = await ApiKeyRepository
|
||||
.Query()
|
||||
.Where(x => x.Key == authHeaderValue)
|
||||
.Select(x => new ApiKeySession(x.Permissions, x.ValidUntil))
|
||||
.FirstOrDefaultAsync(cancellationToken: ct);
|
||||
|
||||
MemoryCache.Set(cacheKey, apiKey, Options.LookupCacheTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (apiKey == null)
|
||||
return AuthenticateResult.Fail("Invalid api key specified");
|
||||
}
|
||||
Console.WriteLine($"API: {x?.ValidUntil}");
|
||||
|
||||
return x;
|
||||
},
|
||||
new HybridCacheEntryOptions()
|
||||
{
|
||||
LocalCacheExpiration = Options.LookupL1CacheTime,
|
||||
Expiration = Options.LookupL2CacheTime
|
||||
}
|
||||
);
|
||||
|
||||
if (apiKey == null)
|
||||
return AuthenticateResult.Fail("Invalid api key specified");
|
||||
|
||||
if (DateTimeOffset.UtcNow > apiKey.ValidUntil)
|
||||
return AuthenticateResult.Fail("Api key expired");
|
||||
|
||||
@@ -4,5 +4,6 @@ namespace Moonlight.Api.Implementations.ApiKeyScheme;
|
||||
|
||||
public class ApiKeySchemeOptions : AuthenticationSchemeOptions
|
||||
{
|
||||
public TimeSpan LookupCacheTime { get; set; }
|
||||
public TimeSpan LookupL1CacheTime { get; set; }
|
||||
public TimeSpan LookupL2CacheTime { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user