diff --git a/Volo.Abp.sln b/Volo.Abp.sln index fc12143408..ff5187c3df 100644 --- a/Volo.Abp.sln +++ b/Volo.Abp.sln @@ -274,6 +274,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Permissions.Applic EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Permissions.Application", "src\Volo.Abp.Permissions.Application\Volo.Abp.Permissions.Application.csproj", "{90197499-CBB6-4C8C-90E6-8718CD755C04}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Caching", "src\Volo.Abp.Caching\Volo.Abp.Caching.csproj", "{A5B650AB-A67F-4A4C-9F81-7B5471CA1331}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -716,6 +718,10 @@ Global {90197499-CBB6-4C8C-90E6-8718CD755C04}.Debug|Any CPU.Build.0 = Debug|Any CPU {90197499-CBB6-4C8C-90E6-8718CD755C04}.Release|Any CPU.ActiveCfg = Release|Any CPU {90197499-CBB6-4C8C-90E6-8718CD755C04}.Release|Any CPU.Build.0 = Release|Any CPU + {A5B650AB-A67F-4A4C-9F81-7B5471CA1331}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5B650AB-A67F-4A4C-9F81-7B5471CA1331}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5B650AB-A67F-4A4C-9F81-7B5471CA1331}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5B650AB-A67F-4A4C-9F81-7B5471CA1331}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -849,6 +855,7 @@ Global {8859CFE4-BC7F-4F29-B6B5-A9457435CC32} = {055F4AC0-46C4-4E99-89CD-0F30D4A97382} {837ABFF5-66EE-486A-8023-8DD3EE7D7D96} = {055F4AC0-46C4-4E99-89CD-0F30D4A97382} {90197499-CBB6-4C8C-90E6-8718CD755C04} = {055F4AC0-46C4-4E99-89CD-0F30D4A97382} + {A5B650AB-A67F-4A4C-9F81-7B5471CA1331} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj b/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj new file mode 100644 index 0000000000..f588132fd6 --- /dev/null +++ b/src/Volo.Abp.Caching/Volo.Abp.Caching.csproj @@ -0,0 +1,24 @@ + + + + + + netstandard2.0 + Volo.Abp.Caching + Volo.Abp.Caching + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + + diff --git a/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs b/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs new file mode 100644 index 0000000000..114f093812 --- /dev/null +++ b/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Caching +{ + public class AbpCachingModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddMemoryCache(); + services.AddDistributedMemoryCache(); + + services.AddAssemblyOf(); + } + } +} diff --git a/src/Volo.Abp.Permissions.Domain/Volo.Abp.Permissions.Domain.csproj b/src/Volo.Abp.Permissions.Domain/Volo.Abp.Permissions.Domain.csproj index 6d5e9b7811..efd91d47a7 100644 --- a/src/Volo.Abp.Permissions.Domain/Volo.Abp.Permissions.Domain.csproj +++ b/src/Volo.Abp.Permissions.Domain/Volo.Abp.Permissions.Domain.csproj @@ -15,8 +15,16 @@ + + + + + C:\Users\hikalkan\.nuget\packages\microsoft.extensions.caching.abstractions\2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Abstractions.dll + + + diff --git a/src/Volo.Abp.Permissions.Domain/Volo/Abp/Permissions/AbpPermissionsDomainModule.cs b/src/Volo.Abp.Permissions.Domain/Volo/Abp/Permissions/AbpPermissionsDomainModule.cs index 4a3a9fd172..9d42682bbf 100644 --- a/src/Volo.Abp.Permissions.Domain/Volo/Abp/Permissions/AbpPermissionsDomainModule.cs +++ b/src/Volo.Abp.Permissions.Domain/Volo/Abp/Permissions/AbpPermissionsDomainModule.cs @@ -1,5 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Authorization; +using Volo.Abp.Caching; +using Volo.Abp.Json; using Volo.Abp.Modularity; namespace Volo.Abp.Permissions @@ -7,6 +9,8 @@ namespace Volo.Abp.Permissions [DependsOn(typeof(AbpAuthorizationModule))] [DependsOn(typeof(AbpDddModule))] [DependsOn(typeof(AbpPermissionsDomainSharedModule))] + [DependsOn(typeof(AbpCachingModule))] + [DependsOn(typeof(AbpJsonModule))] public class AbpPermissionsDomainModule : AbpModule { public override void ConfigureServices(IServiceCollection services) diff --git a/src/Volo.Abp.Permissions.Domain/Volo/Abp/Permissions/PermissionGrantCacheItem.cs b/src/Volo.Abp.Permissions.Domain/Volo/Abp/Permissions/PermissionGrantCacheItem.cs new file mode 100644 index 0000000000..43e8db456f --- /dev/null +++ b/src/Volo.Abp.Permissions.Domain/Volo/Abp/Permissions/PermissionGrantCacheItem.cs @@ -0,0 +1,23 @@ +using System; + +namespace Volo.Abp.Permissions +{ + [Serializable] + public class PermissionGrantCacheItem + { + public string Name { get; set; } + + public bool IsGranted { get; set; } + + public PermissionGrantCacheItem() + { + + } + + public PermissionGrantCacheItem(string name, bool isGranted) + { + Name = name; + IsGranted = isGranted; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Permissions.Domain/Volo/Abp/Permissions/PermissionStore.cs b/src/Volo.Abp.Permissions.Domain/Volo/Abp/Permissions/PermissionStore.cs index 79e5533725..ca67c9006c 100644 --- a/src/Volo.Abp.Permissions.Domain/Volo/Abp/Permissions/PermissionStore.cs +++ b/src/Volo.Abp.Permissions.Domain/Volo/Abp/Permissions/PermissionStore.cs @@ -1,21 +1,85 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Distributed; using Volo.Abp.Authorization.Permissions; using Volo.Abp.DependencyInjection; +using Volo.Abp.Json; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Threading; namespace Volo.Abp.Permissions { + /* TODOS: + * - Wrap distributed cache? + * - Add multi-tenancy + * - Add _cancellationTokenProvider support + * - Add object serialization support + * - Add cache invalidation support..? Maybe it's not cache's job! + */ + public class PermissionStore : AbpServiceBase, IPermissionStore, ITransientDependency { private readonly IPermissionGrantRepository _permissionGrantRepository; + private readonly ICurrentTenant _currentTenant; + private readonly ICancellationTokenProvider _cancellationTokenProvider; + private readonly IDistributedCache _distributedCache; + private readonly IJsonSerializer _jsonSerializer; - public PermissionStore(IPermissionGrantRepository permissionGrantRepository) + public PermissionStore( + IPermissionGrantRepository permissionGrantRepository, + IDistributedCache distributedCache, + ICancellationTokenProvider cancellationTokenProvider, + ICurrentTenant currentTenant, + IJsonSerializer jsonSerializer) { _permissionGrantRepository = permissionGrantRepository; + _distributedCache = distributedCache; + _cancellationTokenProvider = cancellationTokenProvider; + _currentTenant = currentTenant; + _jsonSerializer = jsonSerializer; } public async Task IsGrantedAsync(string name, string providerName, string providerKey) { return await _permissionGrantRepository.FindAsync(name, providerName, providerKey) != null; + //return (await GetCacheItemAsync(name, providerName, providerKey)).IsGranted; //TODO: Use cache when invalidation is possible! + } + + private string CalculateCacheKey(string name, string providerName, string providerKey) + { + var key = "P:" + providerName + "_K:" + providerKey + "N:" + name; + + if (_currentTenant.Id.HasValue) + { + key = "T:" + _currentTenant.Id + "_" + key; + } + + return key; + } + + private async Task GetCacheItemAsync(string name, string providerName, string providerKey) + { + var cacheKey = CalculateCacheKey(name, providerName, providerKey); + var cachedString = await _distributedCache.GetStringAsync(cacheKey, _cancellationTokenProvider.Token); + + if (cachedString != null) + { + return _jsonSerializer.Deserialize(cachedString); + } + + var cacheItem = new PermissionGrantCacheItem( + name, + await _permissionGrantRepository.FindAsync(name, providerName, providerKey) != null + ); + + await _distributedCache.SetStringAsync( + cacheKey, + _jsonSerializer.Serialize(cacheItem), + new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(20) }, + _cancellationTokenProvider.Token + ); + + return cacheItem; } } } diff --git a/src/Volo.Abp.Permissions.EntityFrameworkCore/Volo/Abp/Permissions/EntityFrameworkCore/EfCorePermissionGrantRepository.cs b/src/Volo.Abp.Permissions.EntityFrameworkCore/Volo/Abp/Permissions/EntityFrameworkCore/EfCorePermissionGrantRepository.cs index 261781b3b9..e907c82ed7 100644 --- a/src/Volo.Abp.Permissions.EntityFrameworkCore/Volo/Abp/Permissions/EntityFrameworkCore/EfCorePermissionGrantRepository.cs +++ b/src/Volo.Abp.Permissions.EntityFrameworkCore/Volo/Abp/Permissions/EntityFrameworkCore/EfCorePermissionGrantRepository.cs @@ -18,12 +18,19 @@ namespace Volo.Abp.Permissions.EntityFrameworkCore public async Task FindAsync(string name, string providerName, string providerKey) { - return await DbSet.FirstOrDefaultAsync(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey); + return await DbSet.FirstOrDefaultAsync(s => + s.Name == name && + s.ProviderName == providerName && + s.ProviderKey == providerKey + ); } public async Task> GetListAsync(string providerName, string providerKey) { - return await DbSet.Where(s => s.ProviderName == providerName && s.ProviderKey == providerKey).ToListAsync(); + return await DbSet.Where(s => + s.ProviderName == providerName && + s.ProviderKey == providerKey + ).ToListAsync(); } } } diff --git a/src/Volo.Abp/Volo.Abp.csproj b/src/Volo.Abp/Volo.Abp.csproj index 168637c9e8..ced7db9c3c 100644 --- a/src/Volo.Abp/Volo.Abp.csproj +++ b/src/Volo.Abp/Volo.Abp.csproj @@ -16,6 +16,7 @@ + diff --git a/src/Volo.Abp/Volo/Abp/AbpCommonModule.cs b/src/Volo.Abp/Volo/Abp/AbpCommonModule.cs index 12ea477ed8..639e80fecc 100644 --- a/src/Volo.Abp/Volo/Abp/AbpCommonModule.cs +++ b/src/Volo.Abp/Volo/Abp/AbpCommonModule.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.ApiVersioning; using Volo.Abp.Authorization; +using Volo.Abp.Caching; using Volo.Abp.Data; using Volo.Abp.Json; using Volo.Abp.Localization; @@ -27,6 +28,7 @@ namespace Volo.Abp [DependsOn(typeof(AbpVirtualFileSystemModule))] [DependsOn(typeof(AbpApiVersioningAbstractionsModule))] [DependsOn(typeof(AbpMultiTenancyAbstractionsModule))] + [DependsOn(typeof(AbpCachingModule))] public class AbpCommonModule : AbpModule { public override void ConfigureServices(IServiceCollection services) diff --git a/test/Volo.Abp.Identity.Tests/Volo/Abp/Identity/PermissionManager_Tests.cs b/test/Volo.Abp.Identity.Tests/Volo/Abp/Identity/PermissionManager_Tests.cs index fb782aea56..8deb512cad 100644 --- a/test/Volo.Abp.Identity.Tests/Volo/Abp/Identity/PermissionManager_Tests.cs +++ b/test/Volo.Abp.Identity.Tests/Volo/Abp/Identity/PermissionManager_Tests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Shouldly; +using Volo.Abp.Authorization.Permissions; using Volo.Abp.Permissions; using Volo.Abp.Session; using Xunit; @@ -12,10 +13,12 @@ namespace Volo.Abp.Identity public class PermissionManager_Tests : AbpIdentityDomainTestBase { private readonly IPermissionManager _permissionManager; + private readonly IPermissionStore _permissionStore; public PermissionManager_Tests() { _permissionManager = GetRequiredService(); + _permissionStore = GetRequiredService(); } [Fact] @@ -44,8 +47,12 @@ namespace Volo.Abp.Identity public async Task Should_Grant_Permission_To_Role() { (await _permissionManager.GetForRoleAsync("supporter", TestPermissionNames.MyPermission2)).IsGranted.ShouldBeFalse(); + (await _permissionStore.IsGrantedAsync(TestPermissionNames.MyPermission2, RolePermissionValueProvider.ProviderName, "supporter")).ShouldBeFalse(); + await _permissionManager.SetForRoleAsync("supporter", TestPermissionNames.MyPermission2, true); + (await _permissionManager.GetForRoleAsync("supporter", TestPermissionNames.MyPermission2)).IsGranted.ShouldBeTrue(); + (await _permissionStore.IsGrantedAsync(TestPermissionNames.MyPermission2, RolePermissionValueProvider.ProviderName, "supporter")).ShouldBeTrue(); } [Fact]