mirror of
https://github.com/bitwarden/server
synced 2025-12-30 23:23:37 +00:00
138 lines
4.6 KiB
C#
138 lines
4.6 KiB
C#
using System.Collections.Concurrent;
|
|
using Bit.Core.AdminConsole.Entities;
|
|
using Bit.Core.AdminConsole.Entities.Provider;
|
|
using Bit.Core.AdminConsole.Models.Data.Provider;
|
|
using Bit.Core.AdminConsole.Repositories;
|
|
using Bit.Core.Models.Data.Organizations;
|
|
using Bit.Core.Repositories;
|
|
|
|
namespace Bit.Core.AdminConsole.AbilitiesCache;
|
|
|
|
public class VNextInMemoryApplicationCacheService(
|
|
IOrganizationRepository organizationRepository,
|
|
IProviderRepository providerRepository,
|
|
TimeProvider timeProvider) : IVNextInMemoryApplicationCacheService
|
|
{
|
|
private ConcurrentDictionary<Guid, OrganizationAbility> _orgAbilities = new();
|
|
private readonly SemaphoreSlim _orgInitLock = new(1, 1);
|
|
private DateTimeOffset _lastOrgAbilityRefresh = DateTimeOffset.MinValue;
|
|
|
|
private ConcurrentDictionary<Guid, ProviderAbility> _providerAbilities = new();
|
|
private readonly SemaphoreSlim _providerInitLock = new(1, 1);
|
|
private DateTimeOffset _lastProviderAbilityRefresh = DateTimeOffset.MinValue;
|
|
|
|
private readonly TimeSpan _refreshInterval = TimeSpan.FromMinutes(10);
|
|
|
|
public virtual async Task<IDictionary<Guid, OrganizationAbility>> GetOrganizationAbilitiesAsync()
|
|
{
|
|
await InitOrganizationAbilitiesAsync();
|
|
return _orgAbilities;
|
|
}
|
|
|
|
public async Task<OrganizationAbility?> GetOrganizationAbilityAsync(Guid organizationId)
|
|
{
|
|
(await GetOrganizationAbilitiesAsync())
|
|
.TryGetValue(organizationId, out var organizationAbility);
|
|
return organizationAbility;
|
|
}
|
|
|
|
public virtual async Task<IDictionary<Guid, ProviderAbility>> GetProviderAbilitiesAsync()
|
|
{
|
|
await InitProviderAbilitiesAsync();
|
|
return _providerAbilities;
|
|
}
|
|
|
|
public virtual async Task UpsertProviderAbilityAsync(Provider provider)
|
|
{
|
|
await InitProviderAbilitiesAsync();
|
|
_providerAbilities.AddOrUpdate(
|
|
provider.Id,
|
|
static (_, provider) => new ProviderAbility(provider),
|
|
static (_, _, provider) => new ProviderAbility(provider),
|
|
provider);
|
|
}
|
|
|
|
public virtual async Task UpsertOrganizationAbilityAsync(Organization organization)
|
|
{
|
|
await InitOrganizationAbilitiesAsync();
|
|
|
|
_orgAbilities.AddOrUpdate(
|
|
organization.Id,
|
|
static (_, organization) => new OrganizationAbility(organization),
|
|
static (_, _, organization) => new OrganizationAbility(organization),
|
|
organization);
|
|
}
|
|
|
|
public virtual Task DeleteOrganizationAbilityAsync(Guid organizationId)
|
|
{
|
|
_orgAbilities.TryRemove(organizationId, out _);
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public virtual Task DeleteProviderAbilityAsync(Guid providerId)
|
|
{
|
|
_providerAbilities.TryRemove(providerId, out _);
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private async Task InitOrganizationAbilitiesAsync() =>
|
|
await InitAbilitiesAsync<OrganizationAbility>(
|
|
dict => _orgAbilities = dict,
|
|
() => _lastOrgAbilityRefresh,
|
|
dt => _lastOrgAbilityRefresh = dt,
|
|
_orgInitLock,
|
|
async () => await organizationRepository.GetManyAbilitiesAsync(),
|
|
_refreshInterval,
|
|
ability => ability.Id);
|
|
|
|
private async Task InitProviderAbilitiesAsync() =>
|
|
await InitAbilitiesAsync<ProviderAbility>(
|
|
dict => _providerAbilities = dict,
|
|
() => _lastProviderAbilityRefresh,
|
|
dateTime => _lastProviderAbilityRefresh = dateTime,
|
|
_providerInitLock,
|
|
async () => await providerRepository.GetManyAbilitiesAsync(),
|
|
_refreshInterval,
|
|
ability => ability.Id);
|
|
|
|
|
|
private async Task InitAbilitiesAsync<TAbility>(
|
|
Action<ConcurrentDictionary<Guid, TAbility>> setCache,
|
|
Func<DateTimeOffset> getLastRefresh,
|
|
Action<DateTimeOffset> setLastRefresh,
|
|
SemaphoreSlim @lock,
|
|
Func<Task<IEnumerable<TAbility>>> fetchFunc,
|
|
TimeSpan refreshInterval,
|
|
Func<TAbility, Guid> getId)
|
|
{
|
|
if (SkipRefresh())
|
|
{
|
|
return;
|
|
}
|
|
|
|
await @lock.WaitAsync();
|
|
try
|
|
{
|
|
if (SkipRefresh())
|
|
{
|
|
return;
|
|
}
|
|
|
|
var sources = await fetchFunc();
|
|
var abilities = new ConcurrentDictionary<Guid, TAbility>(
|
|
sources.ToDictionary(getId));
|
|
setCache(abilities);
|
|
setLastRefresh(timeProvider.GetUtcNow());
|
|
}
|
|
finally
|
|
{
|
|
@lock.Release();
|
|
}
|
|
|
|
bool SkipRefresh()
|
|
{
|
|
return timeProvider.GetUtcNow() - getLastRefresh() <= refreshInterval;
|
|
}
|
|
}
|
|
}
|