using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Tgstation.Server.Common.Extensions;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Utils;
namespace Tgstation.Server.Host.Security
{
///
sealed class IdentityCache : IIdentityCache, IAsyncDisposable
{
///
/// The for the .
///
readonly IAsyncDelayer asyncDelayer;
///
/// The for the .
///
readonly ILogger logger;
///
/// The map of s to s.
///
readonly Dictionary cachedIdentities;
///
/// Initializes a new instance of the class.
///
/// The value of .
/// The value of .
public IdentityCache(IAsyncDelayer asyncDelayer, ILogger logger)
{
this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
cachedIdentities = new Dictionary();
}
///
public ValueTask DisposeAsync()
{
logger.LogTrace("Disposing...");
return ValueTaskExtensions.WhenAll(cachedIdentities.Select(x => x.Value.DisposeAsync()));
}
///
public async ValueTask CacheSystemIdentity(User user, ISystemIdentity systemIdentity, DateTimeOffset expiry)
{
ArgumentNullException.ThrowIfNull(user);
ArgumentNullException.ThrowIfNull(systemIdentity);
var uid = user.Require(x => x.Id);
var sysId = systemIdentity.Uid;
ValueTask oldIdentityDisposal = ValueTask.CompletedTask;
try
{
lock (cachedIdentities)
{
logger.LogDebug("Caching system identity {sysId} of user {uid}", sysId, uid);
if (cachedIdentities.TryGetValue(uid, out var identCache))
{
logger.LogTrace("Expiring previously cached identity...");
oldIdentityDisposal = identCache.DisposeAsync(); // also clears it out
}
identCache = new IdentityCacheObject(
systemIdentity.Clone(),
asyncDelayer,
() =>
{
logger.LogDebug("Expiring system identity cache for user {uid}", uid);
lock (cachedIdentities)
cachedIdentities.Remove(uid);
},
expiry);
cachedIdentities.Add(uid, identCache);
}
}
finally
{
await oldIdentityDisposal;
}
}
///
public ISystemIdentity LoadCachedIdentity(User user)
{
ArgumentNullException.ThrowIfNull(user);
var uid = user.Require(x => x.Id);
lock (cachedIdentities)
if (cachedIdentities.TryGetValue(uid, out var identity))
return identity.SystemIdentity.Clone();
throw new InvalidOperationException("Cached system identity has expired!");
}
}
}