Browse Source

Implement `ResourcePermissionStore`.

pull/24374/head
maliming 11 months ago
parent
commit
cdc3fdd008
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 40
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStore.cs
  2. 28
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStoreInMemoryCache.cs
  3. 14
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IDynamicPermissionDefinitionStoreInMemoryCache.cs
  4. 48
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IResourcePermissionGrantRepository.cs
  5. 34
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionGrantCacheItem.cs
  6. 249
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionStore.cs

40
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStore.cs

@ -23,7 +23,7 @@ public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStor
protected IAbpDistributedLock DistributedLock { get; } protected IAbpDistributedLock DistributedLock { get; }
public PermissionManagementOptions PermissionManagementOptions { get; } public PermissionManagementOptions PermissionManagementOptions { get; }
protected AbpDistributedCacheOptions CacheOptions { get; } protected AbpDistributedCacheOptions CacheOptions { get; }
public DynamicPermissionDefinitionStore( public DynamicPermissionDefinitionStore(
IPermissionGroupDefinitionRecordRepository permissionGroupRepository, IPermissionGroupDefinitionRecordRepository permissionGroupRepository,
IPermissionDefinitionRecordRepository permissionRepository, IPermissionDefinitionRecordRepository permissionRepository,
@ -72,6 +72,34 @@ public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStor
} }
} }
public virtual async Task<PermissionDefinition> GetResourcePermissionOrNullAsync(string name)
{
if (!PermissionManagementOptions.IsDynamicPermissionStoreEnabled)
{
return null;
}
using (await StoreCache.SyncSemaphore.LockAsync())
{
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetPermissionOrNull(name);
}
}
public virtual async Task<IReadOnlyList<PermissionDefinition>> GetResourcePermissionsAsync()
{
if (!PermissionManagementOptions.IsDynamicPermissionStoreEnabled)
{
return Array.Empty<PermissionDefinition>();
}
using (await StoreCache.SyncSemaphore.LockAsync())
{
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetPermissions().ToImmutableList();
}
}
public virtual async Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync() public virtual async Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync()
{ {
if (!PermissionManagementOptions.IsDynamicPermissionStoreEnabled) if (!PermissionManagementOptions.IsDynamicPermissionStoreEnabled)
@ -94,9 +122,9 @@ public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStor
/* We get the latest permission with a small delay for optimization */ /* We get the latest permission with a small delay for optimization */
return; return;
} }
var stampInDistributedCache = await GetOrSetStampInDistributedCache(); var stampInDistributedCache = await GetOrSetStampInDistributedCache();
if (stampInDistributedCache == StoreCache.CacheStamp) if (stampInDistributedCache == StoreCache.CacheStamp)
{ {
StoreCache.LastCheckTime = DateTime.Now; StoreCache.LastCheckTime = DateTime.Now;
@ -145,7 +173,7 @@ public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStor
} }
stampInDistributedCache = Guid.NewGuid().ToString(); stampInDistributedCache = Guid.NewGuid().ToString();
await DistributedCache.SetStringAsync( await DistributedCache.SetStringAsync(
cacheKey, cacheKey,
stampInDistributedCache, stampInDistributedCache,
@ -163,9 +191,9 @@ public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStor
{ {
return $"{CacheOptions.KeyPrefix}_AbpInMemoryPermissionCacheStamp"; return $"{CacheOptions.KeyPrefix}_AbpInMemoryPermissionCacheStamp";
} }
protected virtual string GetCommonDistributedLockKey() protected virtual string GetCommonDistributedLockKey()
{ {
return $"{CacheOptions.KeyPrefix}_Common_AbpPermissionUpdateLock"; return $"{CacheOptions.KeyPrefix}_Common_AbpPermissionUpdateLock";
} }
} }

28
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStoreInMemoryCache.cs

@ -18,6 +18,7 @@ public class DynamicPermissionDefinitionStoreInMemoryCache :
protected IDictionary<string, PermissionGroupDefinition> PermissionGroupDefinitions { get; } protected IDictionary<string, PermissionGroupDefinition> PermissionGroupDefinitions { get; }
protected IDictionary<string, PermissionDefinition> PermissionDefinitions { get; } protected IDictionary<string, PermissionDefinition> PermissionDefinitions { get; }
protected IDictionary<string, PermissionDefinition> ResourcePermissionDefinitions { get; }
protected ISimpleStateCheckerSerializer StateCheckerSerializer { get; } protected ISimpleStateCheckerSerializer StateCheckerSerializer { get; }
protected ILocalizableStringSerializer LocalizableStringSerializer { get; } protected ILocalizableStringSerializer LocalizableStringSerializer { get; }
@ -34,6 +35,7 @@ public class DynamicPermissionDefinitionStoreInMemoryCache :
PermissionGroupDefinitions = new Dictionary<string, PermissionGroupDefinition>(); PermissionGroupDefinitions = new Dictionary<string, PermissionGroupDefinition>();
PermissionDefinitions = new Dictionary<string, PermissionDefinition>(); PermissionDefinitions = new Dictionary<string, PermissionDefinition>();
ResourcePermissionDefinitions = new Dictionary<string, PermissionDefinition>();
} }
public Task FillAsync( public Task FillAsync(
@ -42,9 +44,21 @@ public class DynamicPermissionDefinitionStoreInMemoryCache :
{ {
PermissionGroupDefinitions.Clear(); PermissionGroupDefinitions.Clear();
PermissionDefinitions.Clear(); PermissionDefinitions.Clear();
ResourcePermissionDefinitions.Clear();
var context = new PermissionDefinitionContext(null); var context = new PermissionDefinitionContext(null);
var resourcePermissions = permissionRecords.Where(x => !x.ResourceName.IsNullOrWhiteSpace());
foreach (var resourcePermission in resourcePermissions)
{
context.AddResourcePermission(resourcePermission.Name,
resourcePermission.ResourceName,
resourcePermission.DisplayName != null ? LocalizableStringSerializer.Deserialize(resourcePermission.DisplayName) : null,
resourcePermission.MultiTenancySide,
resourcePermission.IsEnabled);
}
var permissions = permissionRecords.Where(x => x.ResourceName.IsNullOrWhiteSpace()).ToList();
foreach (var permissionGroupRecord in permissionGroupRecords) foreach (var permissionGroupRecord in permissionGroupRecords)
{ {
var permissionGroup = context.AddGroup( var permissionGroup = context.AddGroup(
@ -59,12 +73,12 @@ public class DynamicPermissionDefinitionStoreInMemoryCache :
permissionGroup[property.Key] = property.Value; permissionGroup[property.Key] = property.Value;
} }
var permissionRecordsInThisGroup = permissionRecords var permissionRecordsInThisGroup = permissions
.Where(p => p.GroupName == permissionGroup.Name); .Where(p => p.GroupName == permissionGroup.Name);
foreach (var permissionRecord in permissionRecordsInThisGroup.Where(x => x.ParentName == null)) foreach (var permissionRecord in permissionRecordsInThisGroup.Where(x => x.ParentName == null))
{ {
AddPermissionRecursively(permissionGroup, permissionRecord, permissionRecords); AddPermissionRecursively(permissionGroup, permissionRecord, permissions);
} }
} }
@ -86,6 +100,16 @@ public class DynamicPermissionDefinitionStoreInMemoryCache :
return PermissionGroupDefinitions.Values.ToList(); return PermissionGroupDefinitions.Values.ToList();
} }
public PermissionDefinition GetResourcePermissionOrNull(string name)
{
return ResourcePermissionDefinitions.GetOrDefault(name);
}
public IReadOnlyList<PermissionDefinition> GetResourcePermissions()
{
return ResourcePermissionDefinitions.Values.ToList();
}
private void AddPermissionRecursively(ICanAddChildPermission permissionContainer, private void AddPermissionRecursively(ICanAddChildPermission permissionContainer,
PermissionDefinitionRecord permissionRecord, PermissionDefinitionRecord permissionRecord,
List<PermissionDefinitionRecord> allPermissionRecords) List<PermissionDefinitionRecord> allPermissionRecords)

14
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IDynamicPermissionDefinitionStoreInMemoryCache.cs

@ -9,9 +9,9 @@ namespace Volo.Abp.PermissionManagement;
public interface IDynamicPermissionDefinitionStoreInMemoryCache public interface IDynamicPermissionDefinitionStoreInMemoryCache
{ {
string CacheStamp { get; set; } string CacheStamp { get; set; }
SemaphoreSlim SyncSemaphore { get; } SemaphoreSlim SyncSemaphore { get; }
DateTime? LastCheckTime { get; set; } DateTime? LastCheckTime { get; set; }
Task FillAsync( Task FillAsync(
@ -19,8 +19,12 @@ public interface IDynamicPermissionDefinitionStoreInMemoryCache
List<PermissionDefinitionRecord> permissionRecords); List<PermissionDefinitionRecord> permissionRecords);
PermissionDefinition GetPermissionOrNull(string name); PermissionDefinition GetPermissionOrNull(string name);
IReadOnlyList<PermissionDefinition> GetPermissions(); IReadOnlyList<PermissionDefinition> GetPermissions();
IReadOnlyList<PermissionGroupDefinition> GetGroups(); IReadOnlyList<PermissionGroupDefinition> GetGroups();
}
PermissionDefinition GetResourcePermissionOrNull(string name);
IReadOnlyList<PermissionDefinition> GetResourcePermissions();
}

48
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IResourcePermissionGrantRepository.cs

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace Volo.Abp.PermissionManagement;
public interface IResourcePermissionGrantRepository : IBasicRepository<ResourcePermissionGrant, Guid>
{
Task<ResourcePermissionGrant> FindAsync(
string name,
string resourceName,
string resourceKey,
string providerName,
string providerKey,
CancellationToken cancellationToken = default
);
Task<List<ResourcePermissionGrant>> GetListAsync(
string resourceName,
string resourceKey,
string providerName,
string providerKey,
CancellationToken cancellationToken = default
);
Task<List<ResourcePermissionGrant>> GetListAsync(
string[] names,
string resourceName,
string resourceKey,
string providerName,
string providerKey,
CancellationToken cancellationToken = default
);
Task<List<string>> GetPermissionsAsync(
string resourceName,
string resourceKey,
CancellationToken cancellationToken = default
);
Task<List<string>> GetResourceKeys(
string resourceName,
string name,
CancellationToken cancellationToken = default
);
}

34
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionGrantCacheItem.cs

@ -0,0 +1,34 @@
using System;
using System.Linq;
using Volo.Abp.Text.Formatting;
namespace Volo.Abp.PermissionManagement;
[Serializable]
public class ResourcePermissionGrantCacheItem
{
private const string CacheKeyFormat = "rn:{0},rk:{1},pn:{2},pk:{3},n:{4}";
public bool IsGranted { get; set; }
public ResourcePermissionGrantCacheItem()
{
}
public ResourcePermissionGrantCacheItem(bool isGranted)
{
IsGranted = isGranted;
}
public static string CalculateCacheKey(string name, string resourceName, string resourceKey, string providerName, string providerKey)
{
return string.Format(CacheKeyFormat, resourceName, resourceKey, providerName, providerKey, name);
}
public static string GetPermissionNameFormCacheKeyOrNull(string cacheKey)
{
var result = FormattedStringValueExtracter.Extract(cacheKey, CacheKeyFormat, true);
return result.IsMatch ? result.Matches.Last().Value : null;
}
}

249
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/ResourcePermissionStore.cs

@ -0,0 +1,249 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Authorization.Permissions.Resources;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
namespace Volo.Abp.PermissionManagement;
public class ResourcePermissionStore : IResourcePermissionStore, ITransientDependency
{
public ILogger<ResourcePermissionStore> Logger { get; set; }
protected IResourcePermissionGrantRepository ResourcePermissionGrantRepository { get; }
protected IPermissionDefinitionManager PermissionDefinitionManager { get; }
protected IDistributedCache<ResourcePermissionGrantCacheItem> Cache { get; }
public ResourcePermissionStore(
IResourcePermissionGrantRepository resourcePermissionGrantRepository,
IDistributedCache<ResourcePermissionGrantCacheItem> cache,
IPermissionDefinitionManager permissionDefinitionManager)
{
ResourcePermissionGrantRepository = resourcePermissionGrantRepository;
Cache = cache;
PermissionDefinitionManager = permissionDefinitionManager;
Logger = NullLogger<ResourcePermissionStore>.Instance;
}
public virtual async Task<bool> IsGrantedAsync(string name, string resourceName, string resourceKey, string providerName, string providerKey)
{
return (await GetCacheItemAsync(name, resourceName, resourceKey, providerName, providerKey)).IsGranted;
}
protected virtual async Task<ResourcePermissionGrantCacheItem> GetCacheItemAsync(string name, string resourceName, string resourceKey, string providerName, string providerKey)
{
var cacheKey = CalculateCacheKey(name, providerName, providerKey, resourceName, resourceKey);
Logger.LogDebug($"ResourcePermissionStore.GetCacheItemAsync: {cacheKey}");
var cacheItem = await Cache.GetAsync(cacheKey);
if (cacheItem != null)
{
Logger.LogDebug($"Found in the cache: {cacheKey}");
return cacheItem;
}
Logger.LogDebug($"Not found in the cache: {cacheKey}");
cacheItem = new ResourcePermissionGrantCacheItem(false);
await SetCacheItemsAsync(resourceName, resourceKey, providerName, providerKey, name, cacheItem);
return cacheItem;
}
protected virtual async Task SetCacheItemsAsync(string resourceName, string resourceKey, string providerName, string providerKey, string currentName, ResourcePermissionGrantCacheItem currentCacheItem)
{
using (ResourcePermissionGrantRepository.DisableTracking())
{
var permissions = await PermissionDefinitionManager.GetResourcePermissionsAsync();
Logger.LogDebug($"Getting all granted resource permissions from the repository for resource name,key:{resourceName},{resourceKey} and provider name,key: {providerName},{providerKey}");
var grantedPermissionsHashSet = new HashSet<string>(
(await ResourcePermissionGrantRepository.GetListAsync(resourceName, resourceKey, providerName, providerKey)).Select(p => p.Name)
);
Logger.LogDebug($"Setting the cache items. Count: {permissions.Count}");
var cacheItems = new List<KeyValuePair<string, ResourcePermissionGrantCacheItem>>();
foreach (var permission in permissions)
{
var isGranted = grantedPermissionsHashSet.Contains(permission.Name);
cacheItems.Add(new KeyValuePair<string, ResourcePermissionGrantCacheItem>(
CalculateCacheKey(permission.Name, resourceName, resourceKey, providerName, providerKey),
new ResourcePermissionGrantCacheItem(isGranted))
);
if (permission.Name == currentName)
{
currentCacheItem.IsGranted = isGranted;
}
}
await Cache.SetManyAsync(cacheItems);
Logger.LogDebug($"Finished setting the cache items. Count: {permissions.Count}");
}
}
public virtual async Task<MultiplePermissionGrantResult> IsGrantedAsync(string[] names, string resourceName, string resourceKey, string providerName, string providerKey)
{
Check.NotNullOrEmpty(names, nameof(names));
var result = new MultiplePermissionGrantResult();
if (names.Length == 1)
{
var name = names.First();
result.Result.Add(name,
await IsGrantedAsync(names.First(), resourceName, resourceKey, providerName, providerKey)
? PermissionGrantResult.Granted
: PermissionGrantResult.Undefined);
return result;
}
var cacheItems = await GetCacheItemsAsync(names, resourceName, resourceKey, providerName, providerKey);
foreach (var item in cacheItems)
{
result.Result.Add(GetPermissionNameFormCacheKeyOrNull(item.Key),
item.Value != null && item.Value.IsGranted
? PermissionGrantResult.Granted
: PermissionGrantResult.Undefined);
}
return result;
}
protected virtual async Task<List<KeyValuePair<string, ResourcePermissionGrantCacheItem>>> GetCacheItemsAsync(string[] names, string resourceName, string resourceKey, string providerName, string providerKey)
{
var cacheKeys = names.Select(x => CalculateCacheKey(x, resourceName, resourceKey, providerName, providerKey)).ToList();
Logger.LogDebug($"ResourcePermissionStore.GetCacheItemAsync: {string.Join(",", cacheKeys)}");
var cacheItems = (await Cache.GetManyAsync(cacheKeys)).ToList();
if (cacheItems.All(x => x.Value != null))
{
Logger.LogDebug($"Found in the cache: {string.Join(",", cacheKeys)}");
return cacheItems;
}
var notCacheKeys = cacheItems.Where(x => x.Value == null).Select(x => x.Key).ToList();
Logger.LogDebug($"Not found in the cache: {string.Join(",", notCacheKeys)}");
var newCacheItems = await SetCacheItemsAsync(resourceName, resourceKey, providerName, providerKey, notCacheKeys);
var result = new List<KeyValuePair<string, ResourcePermissionGrantCacheItem>>();
foreach (var key in cacheKeys)
{
var item = newCacheItems.FirstOrDefault(x => x.Key == key);
if (item.Value == null)
{
item = cacheItems.FirstOrDefault(x => x.Key == key);
}
result.Add(new KeyValuePair<string, ResourcePermissionGrantCacheItem>(key, item.Value));
}
return result;
}
protected virtual async Task<List<KeyValuePair<string, ResourcePermissionGrantCacheItem>>> SetCacheItemsAsync(string resourceName, string resourceKey, string providerName, string providerKey, List<string> notCacheKeys)
{
using (ResourcePermissionGrantRepository.DisableTracking())
{
var permissionNames = new HashSet<string>(notCacheKeys.Select(GetPermissionNameFormCacheKeyOrNull));
var permissions = (await PermissionDefinitionManager.GetResourcePermissionsAsync())
.Where(x => permissionNames.Contains(x.Name))
.ToList();
Logger.LogDebug($"Getting not cache granted permissions from the repository for resource name,key:{resourceName},{resourceKey} and provider name,key: {providerName},{providerKey}");
var grantedPermissionsHashSet = new HashSet<string>(
(await ResourcePermissionGrantRepository.GetListAsync(permissionNames.ToArray(), resourceName, resourceKey, providerName, providerKey)).Select(p => p.Name)
);
Logger.LogDebug($"Setting the cache items. Count: {permissions.Count}");
var cacheItems = new List<KeyValuePair<string, ResourcePermissionGrantCacheItem>>();
foreach (var permission in permissions)
{
var isGranted = grantedPermissionsHashSet.Contains(permission.Name);
cacheItems.Add(new KeyValuePair<string, ResourcePermissionGrantCacheItem>(
CalculateCacheKey(permission.Name, resourceName, resourceKey, providerName, providerKey),
new ResourcePermissionGrantCacheItem(isGranted))
);
}
await Cache.SetManyAsync(cacheItems);
Logger.LogDebug($"Finished setting the cache items. Count: {permissions.Count}");
return cacheItems;
}
}
public virtual async Task<MultiplePermissionGrantResult> GetPermissionsAsync(string resourceName, string resourceKey)
{
using (ResourcePermissionGrantRepository.DisableTracking())
{
var result = new MultiplePermissionGrantResult();
var resourcePermissions = (await PermissionDefinitionManager.GetResourcePermissionsAsync()).Where(x => x.ResourceName == resourceName).ToList();
var permissionGrants = await ResourcePermissionGrantRepository.GetPermissionsAsync(resourceName, resourceKey);
foreach (var resourcePermission in resourcePermissions)
{
var isGranted = permissionGrants.Any(x => x == resourcePermission.Name);
result.Result.Add(resourcePermission.Name, isGranted ? PermissionGrantResult.Granted : PermissionGrantResult.Undefined);
}
return result;
}
}
public virtual async Task<string[]> GetGrantedPermissionsAsync(string resourceName, string resourceKey)
{
var resourcePermissions = (await PermissionDefinitionManager.GetResourcePermissionsAsync()).Where(x => x.ResourceName == resourceName).ToList();
var grantedPermissions = await ResourcePermissionGrantRepository.GetPermissionsAsync(resourceName, resourceKey);
var result = new List<string>();
foreach (var grantedPermission in grantedPermissions)
{
if (resourcePermissions.Any(x => x.Name == grantedPermission))
{
result.Add(grantedPermission);
}
}
return result.ToArray();
}
public virtual async Task<string[]> GetGrantedResourceKeysAsync(string resourceName, string name)
{
return (await ResourcePermissionGrantRepository.GetResourceKeys(resourceName, name)).ToArray();
}
protected virtual string GetPermissionNameFormCacheKeyOrNull(string key)
{
//TODO: throw ex when name is null?
return ResourcePermissionGrantCacheItem.GetPermissionNameFormCacheKeyOrNull(key);
}
protected virtual string CalculateCacheKey(string name, string resourceName, string resourceKey, string providerName, string providerKey)
{
return ResourcePermissionGrantCacheItem.CalculateCacheKey(name, resourceName, resourceKey, providerName, providerKey);
}
}
Loading…
Cancel
Save