From 7cc725ffa915fc21935269f2b57e9e2f34702ef8 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 25 Feb 2021 09:59:44 +0800 Subject: [PATCH] Use IDistributedCache in AbpCorsPolicyService,ClientStore,ResourceStore. Resolve #6969 https://identityserver4.readthedocs.io/en/latest/reference/options.html#caching --- .../IdentityServer/AbpCorsPolicyService.cs | 14 +- .../Abp/IdentityServer/Clients/ClientStore.cs | 32 +++- .../IdentityServerCacheItemInvalidator.cs | 74 +++++++++ .../Volo/Abp/IdentityServer/ResourceStore.cs | 141 +++++++++++++++--- ...dentityServerCacheItemInvalidator_Tests.cs | 138 +++++++++++++++++ .../ResourceStore_Cache_Tests.cs | 79 ++++++++++ .../AbpIdentityServerTestDataBuilder.cs | 3 + 7 files changed, 459 insertions(+), 22 deletions(-) create mode 100644 modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityServerCacheItemInvalidator.cs create mode 100644 modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/Cache/IdentityServerCacheItemInvalidator_Tests.cs create mode 100644 modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/ResourceStore_Cache_Tests.cs diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpCorsPolicyService.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpCorsPolicyService.cs index 27504ab5ac..57158db720 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpCorsPolicyService.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpCorsPolicyService.cs @@ -5,6 +5,9 @@ using Microsoft.Extensions.Logging.Abstractions; using System; using System.Linq; using System.Threading.Tasks; +using IdentityServer4.Configuration; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Options; using Volo.Abp.Caching; using Volo.Abp.DependencyInjection; using Volo.Abp.IdentityServer.Clients; @@ -16,19 +19,26 @@ namespace Volo.Abp.IdentityServer public ILogger Logger { get; set; } protected IHybridServiceScopeFactory HybridServiceScopeFactory { get; } protected IDistributedCache Cache { get; } + protected IdentityServerOptions Options { get; } public AbpCorsPolicyService( IDistributedCache cache, - IHybridServiceScopeFactory hybridServiceScopeFactory) + IHybridServiceScopeFactory hybridServiceScopeFactory, + IOptions options) { Cache = cache; HybridServiceScopeFactory = hybridServiceScopeFactory; + Options = options.Value; Logger = NullLogger.Instance; } public virtual async Task IsOriginAllowedAsync(string origin) { - var cacheItem = await Cache.GetOrAddAsync(AllowedCorsOriginsCacheItem.AllOrigins, CreateCacheItemAsync); + var cacheItem = await Cache.GetOrAddAsync(AllowedCorsOriginsCacheItem.AllOrigins, CreateCacheItemAsync, + () => new DistributedCacheEntryOptions() + { + AbsoluteExpirationRelativeToNow = Options.Caching.CorsExpiration + }); var isAllowed = cacheItem.AllowedOrigins.Contains(origin, StringComparer.OrdinalIgnoreCase); diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientStore.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientStore.cs index 100d0e4184..d882fdbc52 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientStore.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientStore.cs @@ -1,5 +1,9 @@ using System.Threading.Tasks; +using IdentityServer4.Configuration; using IdentityServer4.Stores; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Options; +using Volo.Abp.Caching; using Volo.Abp.ObjectMapping; namespace Volo.Abp.IdentityServer.Clients @@ -8,17 +12,39 @@ namespace Volo.Abp.IdentityServer.Clients { protected IClientRepository ClientRepository { get; } protected IObjectMapper ObjectMapper { get; } + protected IDistributedCache Cache { get; } + protected IdentityServerOptions Options { get; } - public ClientStore(IClientRepository clientRepository, IObjectMapper objectMapper) + public ClientStore( + IClientRepository clientRepository, + IObjectMapper objectMapper, + IDistributedCache cache, + IOptions options) { ClientRepository = clientRepository; ObjectMapper = objectMapper; + Cache = cache; + Options = options.Value; } public virtual async Task FindClientByIdAsync(string clientId) { - var client = await ClientRepository.FindByClientIdAsync(clientId); - return ObjectMapper.Map(client); + return await GetCacheItemAsync(clientId); + } + + protected virtual async Task GetCacheItemAsync(string clientId) + { + return await Cache.GetOrAddAsync(clientId, async () => + { + var client = await ClientRepository.FindByClientIdAsync(clientId); + return ObjectMapper.Map(client); + }, + optionsFactory: () => new DistributedCacheEntryOptions() + { + AbsoluteExpirationRelativeToNow = Options.Caching.ClientStoreExpiration + }, + considerUow: true); + } } } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityServerCacheItemInvalidator.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityServerCacheItemInvalidator.cs new file mode 100644 index 0000000000..4db0dac9bb --- /dev/null +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityServerCacheItemInvalidator.cs @@ -0,0 +1,74 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.EventBus; +using Volo.Abp.IdentityServer.Clients; +using Volo.Abp.IdentityServer.ApiResources; +using Volo.Abp.IdentityServer.ApiScopes; +using Volo.Abp.IdentityServer.IdentityResources; + +namespace Volo.Abp.IdentityServer +{ + public class IdentityServerCacheItemInvalidator : + ILocalEventHandler>, + ILocalEventHandler>, + ILocalEventHandler>, + ILocalEventHandler>, + ILocalEventHandler>, + ITransientDependency + { + protected IServiceProvider ServiceProvider { get; } + + public IdentityServerCacheItemInvalidator(IServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider; + } + + public virtual async Task HandleEventAsync(EntityChangedEventData eventData) + { + var clientCache = ServiceProvider.GetRequiredService>(); + await clientCache.RemoveAsync(eventData.Entity.ClientId); + + var corsCache = ServiceProvider.GetRequiredService>(); + await corsCache.RemoveAsync(AllowedCorsOriginsCacheItem.AllOrigins); + } + + public async Task HandleEventAsync(EntityChangedEventData eventData) + { + var corsCache = ServiceProvider.GetRequiredService>(); + await corsCache.RemoveAsync(AllowedCorsOriginsCacheItem.AllOrigins); + } + + public virtual async Task HandleEventAsync(EntityChangedEventData eventData) + { + var cache = ServiceProvider.GetRequiredService>(); + await cache.RemoveAsync(eventData.Entity.Name); + + var resourcesCache = ServiceProvider.GetRequiredService>(); + await resourcesCache.RemoveAsync(ResourceStore.AllResourcesKey); + } + + public virtual async Task HandleEventAsync(EntityChangedEventData eventData) + { + var cache = ServiceProvider.GetRequiredService>(); + await cache.RemoveAsync(ResourceStore.ApiResourceNameCacheKeyPrefix + eventData.Entity.Name); + await cache.RemoveManyAsync(eventData.Entity.Scopes.Select(x => ResourceStore.ApiResourceScopeNameCacheKeyPrefix + x.Scope)); + + var resourcesCache = ServiceProvider.GetRequiredService>(); + await resourcesCache.RemoveAsync(ResourceStore.AllResourcesKey); + } + + public virtual async Task HandleEventAsync(EntityChangedEventData eventData) + { + var cache = ServiceProvider.GetRequiredService>(); + await cache.RemoveAsync(eventData.Entity.Name); + + var resourcesCache = ServiceProvider.GetRequiredService>(); + await resourcesCache.RemoveAsync(ResourceStore.AllResourcesKey); + } + } +} diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ResourceStore.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ResourceStore.cs index 5afaaf4400..a8683dddd9 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ResourceStore.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ResourceStore.cs @@ -1,32 +1,59 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using IdentityServer4.Configuration; using IdentityServer4.Models; using IdentityServer4.Stores; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Options; +using Volo.Abp.Caching; using Volo.Abp.IdentityServer.ApiResources; using Volo.Abp.IdentityServer.ApiScopes; using Volo.Abp.IdentityServer.IdentityResources; using Volo.Abp.ObjectMapping; +using ApiResource = Volo.Abp.IdentityServer.ApiResources.ApiResource; +using ApiScope = Volo.Abp.IdentityServer.ApiScopes.ApiScope; +using IdentityResource = Volo.Abp.IdentityServer.IdentityResources.IdentityResource; namespace Volo.Abp.IdentityServer { public class ResourceStore : IResourceStore { + public const string AllResourcesKey = "AllResources"; + public const string ApiResourceNameCacheKeyPrefix = "ApiResourceName_"; + public const string ApiResourceScopeNameCacheKeyPrefix = "ApiResourceScopeName_"; + protected IIdentityResourceRepository IdentityResourceRepository { get; } protected IApiResourceRepository ApiResourceRepository { get; } protected IApiScopeRepository ApiScopeRepository { get; } protected IObjectMapper ObjectMapper { get; } + protected IDistributedCache IdentityResourceCache { get; } + protected IDistributedCache ApiScopeCache { get; } + protected IDistributedCache ApiResourceCache { get; } + protected IDistributedCache ResourcesCache { get; } + protected IdentityServerOptions Options { get; } public ResourceStore( IIdentityResourceRepository identityResourceRepository, IObjectMapper objectMapper, IApiResourceRepository apiResourceRepository, - IApiScopeRepository apiScopeRepository) + IApiScopeRepository apiScopeRepository, + IDistributedCache identityResourceCache, + IDistributedCache apiScopeCache, + IDistributedCache apiResourceCache, + IDistributedCache resourcesCache, + IOptions options) { IdentityResourceRepository = identityResourceRepository; ObjectMapper = objectMapper; ApiResourceRepository = apiResourceRepository; ApiScopeRepository = apiScopeRepository; + IdentityResourceCache = identityResourceCache; + ApiScopeCache = apiScopeCache; + ApiResourceCache = apiResourceCache; + ResourcesCache = resourcesCache; + Options = options.Value; } /// @@ -34,8 +61,14 @@ namespace Volo.Abp.IdentityServer /// public virtual async Task> FindIdentityResourcesByScopeNameAsync(IEnumerable scopeNames) { - var resource = await IdentityResourceRepository.GetListByScopeNameAsync(scopeNames.ToArray(), includeDetails: true); - return ObjectMapper.Map, List>(resource); + return await GetCacheItemsAsync( + IdentityResourceCache, + scopeNames, + async keys => await IdentityResourceRepository.GetListByScopeNameAsync(keys, includeDetails: true), + (models, cacheKeyPrefix)=> new List>> + { + models.Select(x => new KeyValuePair(AddCachePrefix(x.Name, cacheKeyPrefix), x)) + }); } /// @@ -43,8 +76,14 @@ namespace Volo.Abp.IdentityServer /// public virtual async Task> FindApiScopesByNameAsync(IEnumerable scopeNames) { - var scopes = await ApiScopeRepository.GetListByNameAsync(scopeNames.ToArray(), includeDetails: true); - return ObjectMapper.Map, List>(scopes); + return await GetCacheItemsAsync( + ApiScopeCache, + scopeNames, + async keys => await ApiScopeRepository.GetListByNameAsync(keys, includeDetails: true), + (models, cacheKeyPrefix) => new List>> + { + models.Select(x => new KeyValuePair(AddCachePrefix(x.Name, cacheKeyPrefix), x)) + }); } /// @@ -52,8 +91,16 @@ namespace Volo.Abp.IdentityServer /// public virtual async Task> FindApiResourcesByScopeNameAsync(IEnumerable scopeNames) { - var resources = await ApiResourceRepository.GetListByScopesAsync(scopeNames.ToArray(), includeDetails: true); - return ObjectMapper.Map, List>(resources); + return await GetCacheItemsAsync( + ApiResourceCache, + scopeNames, + async keys => await ApiResourceRepository.GetListByScopesAsync(keys, includeDetails: true), + (models, cacheKeyPrefix) => + { + return models + .Select(model => model.Scopes.Select(scope => new KeyValuePair(AddCachePrefix(scope, cacheKeyPrefix), model)).ToList()) + .Where(scopes => scopes.Any()).Cast>>().ToList(); + }, ApiResourceScopeNameCacheKeyPrefix); } /// @@ -61,8 +108,14 @@ namespace Volo.Abp.IdentityServer /// public virtual async Task> FindApiResourcesByNameAsync(IEnumerable apiResourceNames) { - var resources = await ApiResourceRepository.FindByNameAsync(apiResourceNames.ToArray(), includeDetails: true); - return ObjectMapper.Map, List>(resources); + return await GetCacheItemsAsync( + ApiResourceCache, + apiResourceNames, + async keys => await ApiResourceRepository.FindByNameAsync(keys, includeDetails: true), + (models, cacheKeyPrefix) => new List>> + { + models.Select(x => new KeyValuePair(AddCachePrefix(x.Name, cacheKeyPrefix), x)) + }, ApiResourceNameCacheKeyPrefix); } /// @@ -70,14 +123,68 @@ namespace Volo.Abp.IdentityServer /// public virtual async Task GetAllResourcesAsync() { - var identityResources = await IdentityResourceRepository.GetListAsync(includeDetails: true); - var apiResources = await ApiResourceRepository.GetListAsync(includeDetails: true); - var apiScopes = await ApiScopeRepository.GetListAsync(includeDetails: true); - - return new Resources( - ObjectMapper.Map, List>(identityResources), - ObjectMapper.Map, List>(apiResources), - ObjectMapper.Map, List>(apiScopes)); + return await ResourcesCache.GetOrAddAsync(AllResourcesKey, async () => + { + var identityResources = await IdentityResourceRepository.GetListAsync(includeDetails: true); + var apiResources = await ApiResourceRepository.GetListAsync(includeDetails: true); + var apiScopes = await ApiScopeRepository.GetListAsync(includeDetails: true); + + return new Resources( + ObjectMapper.Map, List>(identityResources), + ObjectMapper.Map, List>(apiResources), + ObjectMapper.Map, List>(apiScopes)); + }, () => new DistributedCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = Options.Caching.ClientStoreExpiration + }); + } + + protected virtual async Task> GetCacheItemsAsync( + IDistributedCache cache, + IEnumerable keys, + Func>> entityFactory, + Func, string, List>>> cacheItemsFactory, + string cacheKeyPrefix = null) + where TModel : class + { + var cacheItems = await cache.GetManyAsync(AddCachePrefix(keys, cacheKeyPrefix)); + if (cacheItems.All(x => x.Value != null)) + { + return cacheItems.Select(x => x.Value); + } + + var otherKeys = RemoveCachePrefix(cacheItems.Where(x => x.Value == null).Select(x => x.Key), cacheKeyPrefix).ToArray(); + var otherModels = ObjectMapper.Map, List>(await entityFactory(otherKeys)); + var otherCacheItems = cacheItemsFactory(otherModels, cacheKeyPrefix).ToList(); + foreach (var item in otherCacheItems) + { + await cache.SetManyAsync(item, new DistributedCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = Options.Caching.ClientStoreExpiration + }); + } + + return cacheItems.Where(x => x.Value != null).Select(x => x.Value).Concat(otherModels); + } + + protected virtual IEnumerable AddCachePrefix(IEnumerable keys, string prefix) + { + return prefix == null ? keys : keys.Select(x => AddCachePrefix(x, prefix)); + } + + protected virtual string AddCachePrefix(string key, string prefix) + { + return prefix == null ? key : prefix + key; + } + + protected virtual IEnumerable RemoveCachePrefix(IEnumerable keys, string prefix) + { + return prefix == null ? keys : keys.Select(x => RemoveCachePrefix(x, prefix)); + } + + protected virtual string RemoveCachePrefix(string key, string prefix) + { + return prefix == null ? key : key.RemovePreFix(prefix); } } } diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/Cache/IdentityServerCacheItemInvalidator_Tests.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/Cache/IdentityServerCacheItemInvalidator_Tests.cs new file mode 100644 index 0000000000..2d5498061a --- /dev/null +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/Cache/IdentityServerCacheItemInvalidator_Tests.cs @@ -0,0 +1,138 @@ +using System.Threading.Tasks; +using IdentityServer4.Models; +using IdentityServer4.Services; +using IdentityServer4.Stores; +using Shouldly; +using Volo.Abp.Caching; +using Volo.Abp.IdentityServer.ApiResources; +using Volo.Abp.IdentityServer.ApiScopes; +using Volo.Abp.IdentityServer.Clients; +using Volo.Abp.IdentityServer.IdentityResources; +using Xunit; +using ApiResource = IdentityServer4.Models.ApiResource; +using ApiScope = IdentityServer4.Models.ApiScope; +using Client = IdentityServer4.Models.Client; +using IdentityResource = IdentityServer4.Models.IdentityResource; + +namespace Volo.Abp.IdentityServer.Cache +{ + public class IdentityServerCacheItemInvalidator_Tests : AbpIdentityServerTestBase + { + private readonly IClientStore _clientStore; + private readonly IResourceStore _resourceStore; + + private readonly IClientRepository _clientRepository; + private readonly IIdentityResourceRepository _identityResourceRepository; + private readonly IApiResourceRepository _apiResourceRepository; + private readonly IApiScopeRepository _apiScopeRepository; + + private readonly IDistributedCache _clientCache; + private readonly IDistributedCache _identityResourceCache; + private readonly IDistributedCache_apiResourceCache; + private readonly IDistributedCache _apiScopeCache; + private readonly IDistributedCache _resourceCache; + + private readonly AbpIdentityServerTestData _testData; + + public IdentityServerCacheItemInvalidator_Tests() + { + _clientStore = GetRequiredService(); + _resourceStore = GetRequiredService(); + + _clientRepository = GetRequiredService(); + _identityResourceRepository = GetRequiredService(); + _apiResourceRepository = GetRequiredService(); + _apiScopeRepository = GetRequiredService(); + + _clientCache = GetRequiredService>(); + _identityResourceCache = GetRequiredService>(); + _apiResourceCache = GetRequiredService>(); + _apiScopeCache = GetRequiredService>(); + _resourceCache = GetRequiredService>(); + + _testData = GetRequiredService(); + } + + [Fact] + public async Task Models_Should_Cached_And_Invalidator_When_Its_Changed() + { + //client + var clientId = "ClientId1"; + (await _clientCache.GetAsync(clientId)).ShouldBeNull(); + + var client = await _clientStore.FindClientByIdAsync(clientId); + client.ShouldNotBeNull(); + + var clientCacheItem = await _clientCache.GetAsync(clientId); + clientCacheItem.ShouldNotBeNull(); + + await _clientRepository.DeleteAsync(_testData.Client1Id); + (await _clientCache.GetAsync(clientId)).ShouldBeNull(); + + + //Api Resource + var newApiResource1 = "NewApiResource1"; + var newApiResource2 = "NewApiResource2"; + var testApiResourceName1 = "Test-ApiResource-Name-1"; + var testApiResourceApiScopeName1 = "Test-ApiResource-ApiScope-Name-1"; + var newApiResources = new[] {newApiResource1, newApiResource2}; + + //FindApiResourcesByNameAsync + (await _apiResourceCache.GetAsync(newApiResource1)).ShouldBeNull(); + (await _apiResourceCache.GetAsync(newApiResource2)).ShouldBeNull(); + await _resourceStore.FindApiResourcesByNameAsync(newApiResources); + (await _apiResourceCache.GetAsync(ResourceStore.ApiResourceNameCacheKeyPrefix + newApiResource1)).ShouldNotBeNull(); + (await _apiResourceCache.GetAsync(ResourceStore.ApiResourceNameCacheKeyPrefix + newApiResource2)).ShouldNotBeNull(); + + var apiResource1 = await _apiResourceRepository.FindByNameAsync(newApiResource1); + await _apiResourceRepository.DeleteAsync(apiResource1); + (await _apiResourceCache.GetAsync(newApiResource1)).ShouldBeNull(); + + var apiResource2 = await _apiResourceRepository.FindByNameAsync(newApiResource2); + await _apiResourceRepository.DeleteAsync(apiResource2); + (await _apiResourceCache.GetAsync(newApiResource2)).ShouldBeNull(); + + //FindApiResourcesByScopeNameAsync + (await _apiResourceCache.GetAsync(ResourceStore.ApiResourceScopeNameCacheKeyPrefix + testApiResourceApiScopeName1)).ShouldBeNull(); + await _resourceStore.FindApiResourcesByScopeNameAsync(new []{ testApiResourceApiScopeName1 }); + (await _apiResourceCache.GetAsync(ResourceStore.ApiResourceScopeNameCacheKeyPrefix + testApiResourceApiScopeName1)).ShouldNotBeNull(); + + var testApiResource1 = await _apiResourceRepository.FindByNameAsync(testApiResourceName1); + await _apiResourceRepository.DeleteAsync(testApiResource1); + (await _apiResourceCache.GetAsync(ResourceStore.ApiResourceScopeNameCacheKeyPrefix + testApiResourceApiScopeName1)).ShouldBeNull(); + + + //Identity Resource + var testIdentityResourceName = "Test-Identity-Resource-Name-1"; + var testIdentityResourceNames = new[] {testIdentityResourceName}; + (await _identityResourceCache.GetAsync(testIdentityResourceName)).ShouldBeNull(); + await _resourceStore.FindIdentityResourcesByScopeNameAsync(testIdentityResourceNames); + (await _identityResourceCache.GetAsync(testIdentityResourceName)).ShouldNotBeNull(); + + var testIdentityResource = await _identityResourceRepository.FindByNameAsync(testIdentityResourceName); + await _identityResourceRepository.DeleteAsync(testIdentityResource); + (await _identityResourceCache.GetAsync(testIdentityResourceName)).ShouldBeNull(); + + + //Api Scope + var testApiScopeName = "Test-ApiScope-Name-1"; + var testApiScopeNames = new[] {testApiScopeName}; + (await _apiScopeCache.GetAsync(testApiScopeName)).ShouldBeNull(); + await _resourceStore.FindApiScopesByNameAsync(testApiScopeNames); + (await _apiScopeCache.GetAsync(testApiScopeName)).ShouldNotBeNull(); + + var testApiScope = await _apiScopeRepository.GetByNameAsync(testApiScopeName); + await _apiScopeRepository.DeleteAsync(testApiScope); + (await _apiScopeCache.GetAsync(testApiScopeName)).ShouldBeNull(); + + + //Resources + (await _resourceCache.GetAsync(ResourceStore.AllResourcesKey)).ShouldBeNull(); + await _resourceStore.GetAllResourcesAsync(); + (await _resourceCache.GetAsync(ResourceStore.AllResourcesKey)).ShouldNotBeNull(); + + await _identityResourceRepository.DeleteAsync(_testData.IdentityResource1Id); + (await _resourceCache.GetAsync(ResourceStore.AllResourcesKey)).ShouldBeNull(); + } + } +} diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/ResourceStore_Cache_Tests.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/ResourceStore_Cache_Tests.cs new file mode 100644 index 0000000000..30f751ebdd --- /dev/null +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/ResourceStore_Cache_Tests.cs @@ -0,0 +1,79 @@ +using System.Linq; +using System.Threading.Tasks; +using IdentityServer4.Stores; +using Shouldly; +using Volo.Abp.IdentityServer.ApiResources; +using Xunit; + +namespace Volo.Abp.IdentityServer +{ + public class ResourceStore_Cache_Tests : AbpIdentityServerDomainTestBase + { + private readonly IResourceStore _resourceStore; + + public ResourceStore_Cache_Tests() + { + _resourceStore = GetRequiredService(); + } + + [Fact] + public async Task FindIdentityResourcesByScopeNameAsync() + { + var identityResources = (await _resourceStore.FindIdentityResourcesByScopeNameAsync(new[] {"Test-Identity-Resource-Name-1"})).ToList(); + identityResources.ShouldNotBeEmpty(); + identityResources.Count.ShouldBe(1); + identityResources.First().Name.ShouldBe("Test-Identity-Resource-Name-1"); + + var identityResources2 = (await _resourceStore.FindIdentityResourcesByScopeNameAsync(new[] {"Test-Identity-Resource-Name-1", "NewIdentityResource1"})).ToList(); + identityResources2.ShouldNotBeEmpty(); + identityResources2.Count.ShouldBe(2); + identityResources2.ShouldContain(x => x.Name == "NewIdentityResource1"); + identityResources2.ShouldContain(x => x.Name == identityResources.First().Name); + } + + [Fact] + public async Task FindApiScopesByNameAsync() + { + var apiScopes1 = (await _resourceStore.FindApiScopesByNameAsync(new[] {"Test-ApiScope-Name-1"})).ToList(); + apiScopes1.ShouldNotBeEmpty(); + apiScopes1.Count.ShouldBe(1); + apiScopes1.First().Name.ShouldBe("Test-ApiScope-Name-1"); + + var apiScopes2 = (await _resourceStore.FindApiScopesByNameAsync(new[] {"Test-ApiScope-Name-1", "Test-ApiScope-Name-2"})).ToList(); + apiScopes2.ShouldNotBeEmpty(); + apiScopes2.Count.ShouldBe(2); + apiScopes2.ShouldContain(x => x.Name == "Test-ApiScope-Name-1"); + apiScopes2.ShouldContain(x => x.Name == apiScopes1.First().Name); + } + + [Fact] + public async Task FindApiResourcesByScopeNameAsync() + { + var apiResources1 = (await _resourceStore.FindApiResourcesByScopeNameAsync(new[] {"Test-ApiResource-ApiScope-Name-1"})).ToList(); + apiResources1.ShouldNotBeEmpty(); + apiResources1.Count.ShouldBe(1); + apiResources1.First().Name.ShouldBe("Test-ApiResource-Name-1"); + + var apiResources2 = (await _resourceStore.FindApiResourcesByScopeNameAsync(new[] {"Test-ApiResource-ApiScope-Name-1", nameof(ApiResourceScope.Scope)})).ToList(); + apiResources2.ShouldNotBeEmpty(); + apiResources2.Count.ShouldBe(2); + apiResources2.ShouldContain(x => x.Name == "Test-ApiResource-Name-1"); + apiResources2.ShouldContain(x => x.Name == apiResources1.First().Name); + } + + [Fact] + public async Task FindApiResourcesByNameAsync() + { + var apiResources1 = (await _resourceStore.FindApiResourcesByNameAsync(new[] {"Test-ApiResource-Name-1"})).ToList(); + apiResources1.ShouldNotBeEmpty(); + apiResources1.Count.ShouldBe(1); + apiResources1.First().Name.ShouldBe("Test-ApiResource-Name-1"); + + var apiResources2 = (await _resourceStore.FindApiResourcesByNameAsync(new[] {"Test-ApiResource-Name-1", "NewApiResource1"})).ToList(); + apiResources2.ShouldNotBeEmpty(); + apiResources2.Count.ShouldBe(2); + apiResources2.ShouldContain(x => x.Name == "NewApiResource1"); + apiResources2.ShouldContain(x => x.Name == apiResources1.First().Name); + } + } +} diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs index 721caa1a7f..a2d9490404 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs @@ -75,6 +75,9 @@ namespace Volo.Abp.IdentityServer apiScope.AddUserClaim("Test-ApiScope-Claim-Type-1"); await _apiScopeRepository.InsertAsync(apiScope); + + var apiScope2 = new ApiScope(_guidGenerator.Create(), "Test-ApiScope-Name-2"); + await _apiScopeRepository.InsertAsync(apiScope2); } private async Task AddApiResources()