using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Model.QuickConnect;
namespace MediaBrowser.Controller.QuickConnect;
///
/// A process-local , and the single-instance default. Nothing it holds
/// is visible to another instance, so a deployment running more than one has to configure a shared
/// store instead.
///
public sealed class InMemoryQuickConnectStore : IQuickConnectStore
{
private readonly ConcurrentDictionary> _requests = new(StringComparer.Ordinal);
private readonly ConcurrentDictionary> _authorizations = new(StringComparer.Ordinal);
private readonly ConcurrentDictionary _authorizationClaims = new(StringComparer.Ordinal);
///
public Task GetRequestBySecretAsync(string secret, CancellationToken cancellationToken = default)
{
Expire();
return Task.FromResult(_requests.TryGetValue(secret, out var entry) ? entry.Value : null);
}
///
public Task GetRequestByCodeAsync(string code, CancellationToken cancellationToken = default)
{
Expire();
return Task.FromResult(_requests.Values
.Select(entry => entry.Value)
.FirstOrDefault(request => string.Equals(request.Code, code, StringComparison.Ordinal)));
}
///
public Task SetRequestAsync(QuickConnectResult request, DateTime expiresUtc, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
Expire();
if (expiresUtc > DateTime.UtcNow)
{
_requests[request.Secret] = new Entry(expiresUtc, request);
}
return Task.CompletedTask;
}
///
public Task TryClaimAuthorizationAsync(string secret, DateTime expiresUtc, CancellationToken cancellationToken = default)
{
Expire();
if (!_requests.TryGetValue(secret, out var entry) || entry.Value.Authenticated)
{
return Task.FromResult(false);
}
return Task.FromResult(_authorizationClaims.TryAdd(secret, expiresUtc));
}
///
public Task SetAuthorizationAsync(string secret, AuthenticationResult authenticationResult, DateTime expiresUtc, CancellationToken cancellationToken = default)
{
Expire();
if (expiresUtc > DateTime.UtcNow)
{
_authorizations[secret] = new Entry(expiresUtc, authenticationResult);
}
return Task.CompletedTask;
}
///
public Task GetAuthorizationAsync(string secret, CancellationToken cancellationToken = default)
{
Expire();
return Task.FromResult(_authorizations.TryGetValue(secret, out var entry) ? entry.Value : null);
}
private void Expire()
{
var now = DateTime.UtcNow;
foreach (var (secret, entry) in _requests)
{
if (entry.ExpiresUtc <= now)
{
_requests.TryRemove(secret, out _);
}
}
foreach (var (secret, entry) in _authorizations)
{
if (entry.ExpiresUtc <= now)
{
_authorizations.TryRemove(secret, out _);
}
}
foreach (var (secret, expiresUtc) in _authorizationClaims)
{
if (expiresUtc <= now)
{
_authorizationClaims.TryRemove(secret, out _);
}
}
}
private sealed record Entry(DateTime ExpiresUtc, T Value);
}