Browse Source

Merge pull request #13644 from abpframework/dynamic-permissions

Dynamic permissions
pull/13825/head
maliming 4 years ago
committed by GitHub
parent
commit
f781b42903
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
  2. 17
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs
  3. 14
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/ICanAddChildPermission.cs
  4. 3
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs
  5. 13
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs
  6. 14
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs
  7. 5
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs
  8. 14
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionGroupDefinition.cs
  9. 20
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationPolicyProvider.cs
  10. 38
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/AuthenticatedSimpleStateCheckerSerializerContributor.cs
  11. 13
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IDynamicPermissionDefinitionStore.cs
  12. 13
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IStaticPermissionDefinitionStore.cs
  13. 33
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/NullDynamicPermissionDefinitionStore.cs
  14. 20
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs
  15. 139
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs
  16. 62
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionsSimpleStateCheckerSerializerContributor.cs
  17. 4
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleStateChecker.cs
  18. 122
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs
  19. 6
      framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionApplicationExtensions.cs
  20. 5
      framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
  21. 3
      framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationCreationOptions.cs
  22. 2
      framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithInternalServiceProvider.cs
  23. 14
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IRootServiceProviderAccessor.cs
  24. 19
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/RootServiceProvider.cs
  25. 9
      framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs
  26. 11
      framework/src/Volo.Abp.Core/Volo/Abp/IApplicationNameAccessor.cs
  27. 2
      framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateChecker.cs
  28. 12
      framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializer.cs
  29. 15
      framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializerContributor.cs
  30. 50
      framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializer.cs
  31. 90
      framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializerExtensions.cs
  32. 1
      framework/src/Volo.Abp.DistributedLocking/Volo.Abp.DistributedLocking.csproj
  33. 4
      framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/AbpDistributedLockingModule.cs
  34. 16
      framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/MedallionAbpDistributedLock.cs
  35. 56
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeaturesSimpleStateCheckerSerializerContributor.cs
  36. 10
      framework/src/Volo.Abp.Features/Volo/Abp/Features/RequireFeaturesSimpleStateChecker.cs
  37. 56
      framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor.cs
  38. 18
      framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/RequireGlobalFeaturesSimpleStateChecker.cs
  39. 25
      framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs
  40. 8
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/ILocalizableStringSerializer.cs
  41. 69
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableStringSerializer.cs
  42. 27
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceDictionary.cs
  43. 2
      framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancySides.cs
  44. 27
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/ExtraPropertyDictionaryExtensions.cs
  45. 11
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs
  46. 2
      framework/src/Volo.Abp.Threading/Volo/Abp/Threading/NullCancellationTokenProvider.cs
  47. 23
      framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingOptions_Tests.cs
  48. 41
      framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AuthenticatedSimpleStateCheckerSerializerContributor_Tests.cs
  49. 4
      framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs
  50. 6
      framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/PermissionStateProvider_Tests.cs
  51. 37
      framework/test/Volo.Abp.Core.Tests/Volo/Abp/AbpApplication_Initialize_Tests.cs
  52. 46
      framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeaturesSimpleStateCheckerSerializerContributor_Tests.cs
  53. 46
      framework/test/Volo.Abp.GlobalFeatures.Tests/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor_Tests.cs
  54. 39
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalizationTestModule.cs
  55. 45
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs
  56. 75
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/LocalizableStringSerializer_Tests.cs
  57. 1
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/LocalizationTestResource.cs
  58. 21
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HasExtraPropertiesExtensions_Tests.cs
  59. 3
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeaturePermissionDefinitionProvider.cs
  60. 4
      modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/PermissionGrantInfoDto.cs
  61. 4
      modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/PermissionGroupDto.cs
  62. 54
      modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs
  63. 49
      modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs
  64. 15
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionDefinitionRecordConsts.cs
  65. 5
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionGrantConsts.cs
  66. 11
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionGroupDefinitionRecordConsts.cs
  67. 4
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo.Abp.PermissionManagement.Domain.csproj
  68. 131
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs
  69. 171
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStore.cs
  70. 127
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/DynamicPermissionDefinitionStoreInMemoryCache.cs
  71. 26
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IDynamicPermissionDefinitionStoreInMemoryCache.cs
  72. 13
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionDefinitionRecordRepository.cs
  73. 19
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionDefinitionSerializer.cs
  74. 9
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionGroupDefinitionRecordRepository.cs
  75. 8
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IStaticPermissionSaver.cs
  76. 7
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs
  77. 175
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionRecord.cs
  78. 106
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer.cs
  79. 84
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGroupDefinitionRecord.cs
  80. 17
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs
  81. 78
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManager.cs
  82. 5
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionStore.cs
  83. 294
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionSaver.cs
  84. 38
      modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementDbContextModelBuilderExtensions.cs
  85. 2
      modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementEntityFrameworkCoreModule.cs
  86. 29
      modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/EfCorePermissionDefinitionRecordRepository.cs
  87. 3
      modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/EfCorePermissionGrantRepository.cs
  88. 16
      modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/EfCorePermissionGroupDefinitionRecordRepository.cs
  89. 4
      modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/IPermissionManagementDbContext.cs
  90. 2
      modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/PermissionManagementDbContext.cs
  91. 10
      modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbContextExtensions.cs
  92. 2
      modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbModule.cs
  93. 4
      modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/IPermissionManagementMongoDbContext.cs
  94. 32
      modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/MongoPermissionDefinitionRecordRepository.cs
  95. 4
      modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/MongoPermissionGrantRepository.cs
  96. 16
      modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/MongoPermissionGroupDefinitionRecordRepository.cs
  97. 2
      modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/PermissionManagementMongoDbContext.cs
  98. 55
      modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml.cs
  99. 2
      modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj
  100. 7
      modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/AbpPermissionManagementTestModule.cs

7
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs

@ -171,7 +171,8 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
foreach (var policyName in policyNames)
{
if (await _defaultAuthorizationPolicyProvider.GetPolicyAsync(policyName) == null && _permissionDefinitionManager.GetOrNull(policyName) != null)
if (await _defaultAuthorizationPolicyProvider.GetPolicyAsync(policyName) == null &&
await _permissionDefinitionManager.GetOrNullAsync(policyName) != null)
{
abpPolicyNames.Add(policyName);
}
@ -214,9 +215,9 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
{
var dictionary = new Dictionary<string, string>();
var localizer = _serviceProvider.GetRequiredService(
var localizer = (IStringLocalizer) _serviceProvider.GetRequiredService(
typeof(IStringLocalizer<>).MakeGenericType(resource.ResourceType)
) as IStringLocalizer;
);
foreach (var localizedString in localizer.GetAllStrings())
{

17
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs

@ -1,4 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
@ -24,4 +26,17 @@ public class AbpAuditingModule : AbpModule
{
context.Services.OnRegistred(AuditingInterceptorRegistrar.RegisterIfNeeded);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var applicationName = context.Services.GetApplicationName();
if (!applicationName.IsNullOrEmpty())
{
Configure<AbpAuditingOptions>(options =>
{
options.ApplicationName = applicationName;
});
}
}
}

14
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/ICanAddChildPermission.cs

@ -0,0 +1,14 @@
using JetBrains.Annotations;
using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Authorization.Permissions;
public interface ICanAddChildPermission
{
PermissionDefinition AddPermission(
[NotNull] string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both,
bool isEnabled = true);
}

3
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs

@ -37,8 +37,7 @@ public interface IPermissionDefinitionContext
/// </summary>
PermissionGroupDefinition AddGroup(
[NotNull] string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both);
ILocalizableString displayName = null);
/// <summary>
/// Tries to remove a permission group.

13
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs

@ -1,17 +1,18 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.Authorization.Permissions;
public interface IPermissionDefinitionManager
{
[NotNull]
PermissionDefinition Get([NotNull] string name);
[ItemNotNull]
Task<PermissionDefinition> GetAsync([NotNull] string name);
[CanBeNull]
PermissionDefinition GetOrNull([NotNull] string name);
[ItemCanBeNull]
Task<PermissionDefinition> GetOrNullAsync([NotNull] string name);
IReadOnlyList<PermissionDefinition> GetPermissions();
Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync();
IReadOnlyList<PermissionGroupDefinition> GetGroups();
Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync();
}

14
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs

@ -7,7 +7,9 @@ using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.Authorization.Permissions;
public class PermissionDefinition : IHasSimpleStateCheckers<PermissionDefinition>
public class PermissionDefinition :
IHasSimpleStateCheckers<PermissionDefinition>,
ICanAddChildPermission
{
/// <summary>
/// Unique name of the permission.
@ -110,6 +112,16 @@ public class PermissionDefinition : IHasSimpleStateCheckers<PermissionDefinition
return child;
}
PermissionDefinition ICanAddChildPermission.AddPermission(
string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both,
bool isEnabled = true)
{
return this.AddChild(name, displayName, multiTenancySide, isEnabled);
}
/// <summary>
/// Sets a property in the <see cref="Properties"/> dictionary.

5
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs

@ -20,8 +20,7 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext
public virtual PermissionGroupDefinition AddGroup(
string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both)
ILocalizableString displayName = null)
{
Check.NotNull(name, nameof(name));
@ -30,7 +29,7 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext
throw new AbpException($"There is already an existing permission group with name: {name}");
}
return Groups[name] = new PermissionGroupDefinition(name, displayName, multiTenancySide);
return Groups[name] = new PermissionGroupDefinition(name, displayName);
}
[NotNull]

14
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionGroupDefinition.cs

@ -6,7 +6,7 @@ using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Authorization.Permissions;
public class PermissionGroupDefinition //TODO: Consider to make possible a group have sub groups
public class PermissionGroupDefinition : ICanAddChildPermission
{
/// <summary>
/// Unique name of the group.
@ -21,12 +21,6 @@ public class PermissionGroupDefinition //TODO: Consider to make possible a group
}
private ILocalizableString _displayName;
/// <summary>
/// MultiTenancy side.
/// Default: <see cref="MultiTenancySides.Both"/>
/// </summary>
public MultiTenancySides MultiTenancySide { get; set; }
public IReadOnlyList<PermissionDefinition> Permissions => _permissions.ToImmutableList();
private readonly List<PermissionDefinition> _permissions;
@ -45,19 +39,17 @@ public class PermissionGroupDefinition //TODO: Consider to make possible a group
protected internal PermissionGroupDefinition(
string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both)
ILocalizableString displayName = null)
{
Name = name;
DisplayName = displayName ?? new FixedLocalizableString(Name);
MultiTenancySide = multiTenancySide;
Properties = new Dictionary<string, object>();
_permissions = new List<PermissionDefinition>();
}
public virtual PermissionDefinition AddPermission(
string name,
[NotNull] string name,
ILocalizableString displayName = null,
MultiTenancySides multiTenancySide = MultiTenancySides.Both,
bool isEnabled = true)

20
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationPolicyProvider.cs

@ -31,7 +31,7 @@ public class AbpAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider
return policy;
}
var permission = _permissionDefinitionManager.GetOrNull(policyName);
var permission = await _permissionDefinitionManager.GetOrNullAsync(policyName);
if (permission != null)
{
//TODO: Optimize & Cache!
@ -43,16 +43,14 @@ public class AbpAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider
return null;
}
public Task<List<string>> GetPoliciesNamesAsync()
public async Task<List<string>> GetPoliciesNamesAsync()
{
return Task.FromResult(
_options.GetPoliciesNames()
.Union(
_permissionDefinitionManager
.GetPermissions()
.Select(p => p.Name)
)
.ToList()
);
return _options.GetPoliciesNames()
.Union(
(await _permissionDefinitionManager
.GetPermissionsAsync())
.Select(p => p.Name)
)
.ToList();
}
}

38
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/AuthenticatedSimpleStateCheckerSerializerContributor.cs

@ -0,0 +1,38 @@
using System.Text.Json.Nodes;
using Volo.Abp.DependencyInjection;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.Authorization.Permissions;
public class AuthenticatedSimpleStateCheckerSerializerContributor :
ISimpleStateCheckerSerializerContributor,
ISingletonDependency
{
public const string CheckerShortName = "A";
public string SerializeToJson<TState>(ISimpleStateChecker<TState> checker)
where TState : IHasSimpleStateCheckers<TState>
{
if (checker is not RequireAuthenticatedSimpleStateChecker<TState>)
{
return null;
}
var jsonObject = new JsonObject {
["T"] = CheckerShortName
};
return jsonObject.ToJsonString();
}
public ISimpleStateChecker<TState> Deserialize<TState>(JsonObject jsonObject, TState state)
where TState : IHasSimpleStateCheckers<TState>
{
if (jsonObject["T"]?.ToString() != CheckerShortName)
{
return null;
}
return new RequireAuthenticatedSimpleStateChecker<TState>();
}
}

13
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IDynamicPermissionDefinitionStore.cs

@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Volo.Abp.Authorization.Permissions;
public interface IDynamicPermissionDefinitionStore
{
Task<PermissionDefinition> GetOrNullAsync(string name);
Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync();
Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync();
}

13
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IStaticPermissionDefinitionStore.cs

@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Volo.Abp.Authorization.Permissions;
public interface IStaticPermissionDefinitionStore
{
Task<PermissionDefinition> GetOrNullAsync(string name);
Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync();
Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync();
}

33
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/NullDynamicPermissionDefinitionStore.cs

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Authorization.Permissions;
public class NullDynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStore, ISingletonDependency
{
private readonly static Task<PermissionDefinition> CachedPermissionResult = Task.FromResult((PermissionDefinition)null);
private readonly static Task<IReadOnlyList<PermissionDefinition>> CachedPermissionsResult =
Task.FromResult((IReadOnlyList<PermissionDefinition>)Array.Empty<PermissionDefinition>().ToImmutableList());
private readonly static Task<IReadOnlyList<PermissionGroupDefinition>> CachedGroupsResult =
Task.FromResult((IReadOnlyList<PermissionGroupDefinition>)Array.Empty<PermissionGroupDefinition>().ToImmutableList());
public Task<PermissionDefinition> GetOrNullAsync(string name)
{
return CachedPermissionResult;
}
public Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync()
{
return CachedPermissionsResult;
}
public Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync()
{
return CachedGroupsResult;
}
}

20
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs

@ -43,8 +43,12 @@ public class PermissionChecker : IPermissionChecker, ITransientDependency
{
Check.NotNull(name, nameof(name));
var permission = PermissionDefinitionManager.Get(name);
var permission = await PermissionDefinitionManager.GetOrNullAsync(name);
if (permission == null)
{
return false;
}
if (!permission.IsEnabled)
{
return false;
@ -97,18 +101,24 @@ public class PermissionChecker : IPermissionChecker, ITransientDependency
{
Check.NotNull(names, nameof(names));
var multiTenancySide = claimsPrincipal?.GetMultiTenancySide() ?? CurrentTenant.GetMultiTenancySide();
var result = new MultiplePermissionGrantResult();
if (!names.Any())
{
return result;
}
var multiTenancySide = claimsPrincipal?.GetMultiTenancySide() ??
CurrentTenant.GetMultiTenancySide();
var permissionDefinitions = new List<PermissionDefinition>();
foreach (var name in names)
{
var permission = PermissionDefinitionManager.Get(name);
var permission = await PermissionDefinitionManager.GetOrNullAsync(name);
if (permission == null)
{
result.Result.Add(name, PermissionGrantResult.Prohibited);
continue;
}
result.Result.Add(name, PermissionGrantResult.Undefined);

139
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs

@ -1,47 +1,27 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Authorization.Permissions;
public class PermissionDefinitionManager : IPermissionDefinitionManager, ISingletonDependency
public class PermissionDefinitionManager : IPermissionDefinitionManager, ITransientDependency
{
protected IDictionary<string, PermissionGroupDefinition> PermissionGroupDefinitions => _lazyPermissionGroupDefinitions.Value;
private readonly Lazy<Dictionary<string, PermissionGroupDefinition>> _lazyPermissionGroupDefinitions;
protected IDictionary<string, PermissionDefinition> PermissionDefinitions => _lazyPermissionDefinitions.Value;
private readonly Lazy<Dictionary<string, PermissionDefinition>> _lazyPermissionDefinitions;
protected AbpPermissionOptions Options { get; }
private readonly IServiceProvider _serviceProvider;
private readonly IStaticPermissionDefinitionStore _staticStore;
private readonly IDynamicPermissionDefinitionStore _dynamicStore;
public PermissionDefinitionManager(
IOptions<AbpPermissionOptions> options,
IServiceProvider serviceProvider)
IStaticPermissionDefinitionStore staticStore,
IDynamicPermissionDefinitionStore dynamicStore)
{
_serviceProvider = serviceProvider;
Options = options.Value;
_lazyPermissionDefinitions = new Lazy<Dictionary<string, PermissionDefinition>>(
CreatePermissionDefinitions,
isThreadSafe: true
);
_lazyPermissionGroupDefinitions = new Lazy<Dictionary<string, PermissionGroupDefinition>>(
CreatePermissionGroupDefinitions,
isThreadSafe: true
);
_staticStore = staticStore;
_dynamicStore = dynamicStore;
}
public virtual PermissionDefinition Get(string name)
public virtual async Task<PermissionDefinition> GetAsync(string name)
{
var permission = GetOrNull(name);
var permission = await GetOrNullAsync(name);
if (permission == null)
{
throw new AbpException("Undefined permission: " + name);
@ -50,82 +30,41 @@ public class PermissionDefinitionManager : IPermissionDefinitionManager, ISingle
return permission;
}
public virtual PermissionDefinition GetOrNull(string name)
public virtual async Task<PermissionDefinition> GetOrNullAsync(string name)
{
Check.NotNull(name, nameof(name));
return PermissionDefinitions.GetOrDefault(name);
}
public virtual IReadOnlyList<PermissionDefinition> GetPermissions()
{
return PermissionDefinitions.Values.ToImmutableList();
}
public IReadOnlyList<PermissionGroupDefinition> GetGroups()
{
return PermissionGroupDefinitions.Values.ToImmutableList();
}
protected virtual Dictionary<string, PermissionDefinition> CreatePermissionDefinitions()
{
var permissions = new Dictionary<string, PermissionDefinition>();
foreach (var groupDefinition in PermissionGroupDefinitions.Values)
{
foreach (var permission in groupDefinition.Permissions)
{
AddPermissionToDictionaryRecursively(permissions, permission);
}
}
return permissions;
return await _staticStore.GetOrNullAsync(name) ??
await _dynamicStore.GetOrNullAsync(name);
}
protected virtual void AddPermissionToDictionaryRecursively(
Dictionary<string, PermissionDefinition> permissions,
PermissionDefinition permission)
public virtual async Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync()
{
if (permissions.ContainsKey(permission.Name))
{
throw new AbpException("Duplicate permission name: " + permission.Name);
}
permissions[permission.Name] = permission;
foreach (var child in permission.Children)
{
AddPermissionToDictionaryRecursively(permissions, child);
}
var staticPermissions = await _staticStore.GetPermissionsAsync();
var staticPermissionNames = staticPermissions
.Select(p => p.Name)
.ToImmutableHashSet();
var dynamicPermissions = await _dynamicStore.GetPermissionsAsync();
/* We prefer static permissions over dynamics */
return staticPermissions.Concat(
dynamicPermissions.Where(d => !staticPermissionNames.Contains(d.Name))
).ToImmutableList();
}
protected virtual Dictionary<string, PermissionGroupDefinition> CreatePermissionGroupDefinitions()
public async Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync()
{
using (var scope = _serviceProvider.CreateScope())
{
var context = new PermissionDefinitionContext(scope.ServiceProvider);
var providers = Options
.DefinitionProviders
.Select(p => scope.ServiceProvider.GetRequiredService(p) as IPermissionDefinitionProvider)
.ToList();
foreach (var provider in providers)
{
provider.PreDefine(context);
}
foreach (var provider in providers)
{
provider.Define(context);
}
foreach (var provider in providers)
{
provider.PostDefine(context);
}
return context.Groups;
}
var staticGroups = await _staticStore.GetGroupsAsync();
var staticGroupNames = staticGroups
.Select(p => p.Name)
.ToImmutableHashSet();
var dynamicGroups = await _dynamicStore.GetGroupsAsync();
/* We prefer static groups over dynamics */
return staticGroups.Concat(
dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name))
).ToImmutableList();
}
}
}

62
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionsSimpleStateCheckerSerializerContributor.cs

@ -0,0 +1,62 @@
using System.Linq;
using System.Text.Json.Nodes;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.GlobalFeatures;
public class PermissionsSimpleStateCheckerSerializerContributor :
ISimpleStateCheckerSerializerContributor,
ISingletonDependency
{
public const string CheckerShortName = "P";
public string SerializeToJson<TState>(ISimpleStateChecker<TState> checker)
where TState : IHasSimpleStateCheckers<TState>
{
if (checker is not RequirePermissionsSimpleStateChecker<TState> permissionsSimpleStateChecker)
{
return null;
}
var jsonObject = new JsonObject {
["T"] = CheckerShortName,
["A"] = permissionsSimpleStateChecker.RequiresAll
};
var nameArray = new JsonArray();
foreach (var permissionName in permissionsSimpleStateChecker.PermissionNames)
{
nameArray.Add(permissionName);
}
jsonObject["N"] = nameArray;
return jsonObject.ToJsonString();
}
public ISimpleStateChecker<TState> Deserialize<TState>(
JsonObject jsonObject,
TState state)
where TState : IHasSimpleStateCheckers<TState>
{
if (jsonObject["T"]?.ToString() != CheckerShortName)
{
return null;
}
var nameArray = jsonObject["N"] as JsonArray;
if (nameArray == null)
{
throw new AbpException("'N' is not an array in the serialized state checker! JsonObject: " + jsonObject.ToJsonString());
}
return new RequirePermissionsSimpleStateChecker<TState>(
new RequirePermissionsSimpleBatchStateCheckerModel<TState>(
state,
nameArray.Select(x => x.ToString()).ToArray(),
(bool?)jsonObject["A"] ?? false
)
);
}
}

4
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleStateChecker.cs

@ -8,6 +8,10 @@ namespace Volo.Abp.Authorization.Permissions;
public class RequirePermissionsSimpleStateChecker<TState> : ISimpleStateChecker<TState>
where TState : IHasSimpleStateCheckers<TState>
{
public bool RequiresAll => _model.RequiresAll;
public string[] PermissionNames => _model.Permissions;
private readonly RequirePermissionsSimpleBatchStateCheckerModel<TState> _model;
public RequirePermissionsSimpleStateChecker(RequirePermissionsSimpleBatchStateCheckerModel<TState> model)

122
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs

@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Authorization.Permissions;
public class StaticPermissionDefinitionStore : IStaticPermissionDefinitionStore, ISingletonDependency
{
protected IDictionary<string, PermissionGroupDefinition> PermissionGroupDefinitions => _lazyPermissionGroupDefinitions.Value;
private readonly Lazy<Dictionary<string, PermissionGroupDefinition>> _lazyPermissionGroupDefinitions;
protected IDictionary<string, PermissionDefinition> PermissionDefinitions => _lazyPermissionDefinitions.Value;
private readonly Lazy<Dictionary<string, PermissionDefinition>> _lazyPermissionDefinitions;
protected AbpPermissionOptions Options { get; }
private readonly IServiceProvider _serviceProvider;
public StaticPermissionDefinitionStore(
IServiceProvider serviceProvider,
IOptions<AbpPermissionOptions> options)
{
_serviceProvider = serviceProvider;
Options = options.Value;
_lazyPermissionDefinitions = new Lazy<Dictionary<string, PermissionDefinition>>(
CreatePermissionDefinitions,
isThreadSafe: true
);
_lazyPermissionGroupDefinitions = new Lazy<Dictionary<string, PermissionGroupDefinition>>(
CreatePermissionGroupDefinitions,
isThreadSafe: true
);
}
protected virtual Dictionary<string, PermissionDefinition> CreatePermissionDefinitions()
{
var permissions = new Dictionary<string, PermissionDefinition>();
foreach (var groupDefinition in PermissionGroupDefinitions.Values)
{
foreach (var permission in groupDefinition.Permissions)
{
AddPermissionToDictionaryRecursively(permissions, permission);
}
}
return permissions;
}
protected virtual void AddPermissionToDictionaryRecursively(
Dictionary<string, PermissionDefinition> permissions,
PermissionDefinition permission)
{
if (permissions.ContainsKey(permission.Name))
{
throw new AbpException("Duplicate permission name: " + permission.Name);
}
permissions[permission.Name] = permission;
foreach (var child in permission.Children)
{
AddPermissionToDictionaryRecursively(permissions, child);
}
}
protected virtual Dictionary<string, PermissionGroupDefinition> CreatePermissionGroupDefinitions()
{
using (var scope = _serviceProvider.CreateScope())
{
var context = new PermissionDefinitionContext(scope.ServiceProvider);
var providers = Options
.DefinitionProviders
.Select(p => scope.ServiceProvider.GetRequiredService(p) as IPermissionDefinitionProvider)
.ToList();
foreach (var provider in providers)
{
provider.PreDefine(context);
}
foreach (var provider in providers)
{
provider.Define(context);
}
foreach (var provider in providers)
{
provider.PostDefine(context);
}
return context.Groups;
}
}
public Task<PermissionDefinition> GetOrNullAsync(string name)
{
return Task.FromResult(PermissionDefinitions.GetOrDefault(name));
}
public virtual Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync()
{
return Task.FromResult<IReadOnlyList<PermissionDefinition>>(
PermissionDefinitions.Values.ToImmutableList()
);
}
public Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync()
{
return Task.FromResult<IReadOnlyList<PermissionGroupDefinition>>(
PermissionGroupDefinitions.Values.ToImmutableList()
);
}
}

6
framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionApplicationExtensions.cs

@ -39,4 +39,10 @@ public static class ServiceCollectionApplicationExtensions
{
return await AbpApplicationFactory.CreateAsync(startupModuleType, services, optionsAction);
}
[CanBeNull]
public static string GetApplicationName(this IServiceCollection services)
{
return services.GetSingletonInstance<IApplicationNameAccessor>().ApplicationName;
}
}

5
framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs

@ -23,6 +23,8 @@ public abstract class AbpApplicationBase : IAbpApplication
public IServiceCollection Services { get; }
public IReadOnlyList<IAbpModuleDescriptor> Modules { get; }
public string ApplicationName { get; }
private bool _configuredServices;
@ -41,8 +43,11 @@ public abstract class AbpApplicationBase : IAbpApplication
var options = new AbpApplicationCreationOptions(services);
optionsAction?.Invoke(options);
ApplicationName = options.ApplicationName;
services.AddSingleton<IAbpApplication>(this);
services.AddSingleton<IApplicationNameAccessor>(this);
services.AddSingleton<IModuleContainer>(this);
services.AddCoreServices();

3
framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationCreationOptions.cs

@ -20,6 +20,9 @@ public class AbpApplicationCreationOptions
public AbpConfigurationBuilderOptions Configuration { get; }
public bool SkipConfigureServices { get; set; }
[CanBeNull]
public string ApplicationName { get; set; }
public AbpApplicationCreationOptions([NotNull] IServiceCollection services)
{

2
framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithInternalServiceProvider.cs

@ -60,6 +60,6 @@ internal class AbpApplicationWithInternalServiceProvider : AbpApplicationBase, I
public override void Dispose()
{
base.Dispose();
ServiceScope.Dispose();
ServiceScope?.Dispose();
}
}

14
framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IRootServiceProviderAccessor.cs

@ -0,0 +1,14 @@
using System;
namespace Volo.Abp.DependencyInjection;
/// <summary>
/// The root service provider of the application.
/// Be careful to use the root service provider since there is no way
/// to release/dispose objects resolved from the root service provider.
/// So, always create a new scope if you need to resolve any service.
/// </summary>
public interface IRootServiceProvider : IServiceProvider
{
}

19
framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/RootServiceProvider.cs

@ -0,0 +1,19 @@
using System;
namespace Volo.Abp.DependencyInjection;
[ExposeServices(typeof(IRootServiceProvider))]
public class RootServiceProvider : IRootServiceProvider, ISingletonDependency
{
protected IServiceProvider ServiceProvider { get; }
public RootServiceProvider(IObjectAccessor<IServiceProvider> objectAccessor)
{
ServiceProvider = objectAccessor.Value;
}
public virtual object GetService(Type serviceType)
{
return ServiceProvider.GetService(serviceType);
}
}

9
framework/src/Volo.Abp.Core/Volo/Abp/IAbpApplication.cs

@ -5,7 +5,10 @@ using Volo.Abp.Modularity;
namespace Volo.Abp;
public interface IAbpApplication : IModuleContainer, IDisposable
public interface IAbpApplication :
IModuleContainer,
IApplicationNameAccessor,
IDisposable
{
/// <summary>
/// Type of the startup (entrance) module of the application.
@ -23,7 +26,7 @@ public interface IAbpApplication : IModuleContainer, IDisposable
/// This can not be used before initialize the application.
/// </summary>
IServiceProvider ServiceProvider { get; }
/// <summary>
/// Calls the Pre/Post/ConfigureServicesAsync methods of the modules.
/// If you use this method, you must have set the <see cref="AbpApplicationCreationOptions.SkipConfigureServices"/>
@ -40,4 +43,4 @@ public interface IAbpApplication : IModuleContainer, IDisposable
/// Used to gracefully shutdown the application and all modules.
/// </summary>
void Shutdown();
}
}

11
framework/src/Volo.Abp.Core/Volo/Abp/IApplicationNameAccessor.cs

@ -0,0 +1,11 @@
namespace Volo.Abp;
public interface IApplicationNameAccessor
{
/// <summary>
/// Name of the application.
/// This is useful for systems with multiple applications, to distinguish
/// resources of the applications located together.
/// </summary>
string ApplicationName { get; }
}

2
framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateChecker.cs

@ -6,4 +6,4 @@ public interface ISimpleStateChecker<TState>
where TState : IHasSimpleStateCheckers<TState>
{
Task<bool> IsEnabledAsync(SimpleStateCheckerContext<TState> context);
}
}

12
framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializer.cs

@ -0,0 +1,12 @@
using System.Text.Json.Nodes;
namespace Volo.Abp.SimpleStateChecking;
public interface ISimpleStateCheckerSerializer
{
public string Serialize<TState>(ISimpleStateChecker<TState> checker)
where TState : IHasSimpleStateCheckers<TState>;
public ISimpleStateChecker<TState> Deserialize<TState>(JsonObject jsonObject, TState state)
where TState : IHasSimpleStateCheckers<TState>;
}

15
framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializerContributor.cs

@ -0,0 +1,15 @@
using System.Text.Json.Nodes;
using JetBrains.Annotations;
namespace Volo.Abp.SimpleStateChecking;
public interface ISimpleStateCheckerSerializerContributor
{
[CanBeNull]
public string SerializeToJson<TState>(ISimpleStateChecker<TState> checker)
where TState : IHasSimpleStateCheckers<TState>;
[CanBeNull]
public ISimpleStateChecker<TState> Deserialize<TState>(JsonObject jsonObject, TState state)
where TState : IHasSimpleStateCheckers<TState>;
}

50
framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializer.cs

@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Text.Json.Nodes;
using JetBrains.Annotations;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.SimpleStateChecking;
public class SimpleStateCheckerSerializer :
ISimpleStateCheckerSerializer,
ISingletonDependency
{
private readonly IEnumerable<ISimpleStateCheckerSerializerContributor> _contributors;
public SimpleStateCheckerSerializer(IEnumerable<ISimpleStateCheckerSerializerContributor> contributors)
{
_contributors = contributors;
}
[CanBeNull]
public string Serialize<TState>(ISimpleStateChecker<TState> checker)
where TState : IHasSimpleStateCheckers<TState>
{
foreach (var contributor in _contributors)
{
var result = contributor.SerializeToJson(checker);
if (result != null)
{
return result;
}
}
return null;
}
[CanBeNull]
public ISimpleStateChecker<TState> Deserialize<TState>(JsonObject jsonObject, TState state)
where TState : IHasSimpleStateCheckers<TState>
{
foreach (var contributor in _contributors)
{
var result = contributor.Deserialize(jsonObject, state);
if (result != null)
{
return result;
}
}
return null;
}
}

90
framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializerExtensions.cs

@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Nodes;
namespace Volo.Abp.SimpleStateChecking;
public static class SimpleStateCheckerSerializerExtensions
{
public static string Serialize<TState>(
this ISimpleStateCheckerSerializer serializer,
IList<ISimpleStateChecker<TState>> stateCheckers)
where TState : IHasSimpleStateCheckers<TState>
{
switch (stateCheckers.Count)
{
case 0:
return null;
case 1:
var serializedChecker = serializer.Serialize(stateCheckers.Single());
return serializedChecker != null
? $"[{serializedChecker}]"
: null;
default:
var serializedCheckers = new List<string>(stateCheckers.Count);
foreach (var stateChecker in stateCheckers)
{
var serialized = serializer.Serialize(stateChecker);
if (serialized != null)
{
serializedCheckers.Add(serialized);
}
}
return serializedCheckers.Any()
? $"[{serializedCheckers.JoinAsString(",")}]"
: null;
}
}
public static ISimpleStateChecker<TState>[] DeserializeArray<TState>(
this ISimpleStateCheckerSerializer serializer,
string value,
TState state)
where TState : IHasSimpleStateCheckers<TState>
{
if (value.IsNullOrWhiteSpace())
{
return Array.Empty<ISimpleStateChecker<TState>>();
}
var array = JsonNode.Parse(value) as JsonArray;
if (array == null || array.Count == 0)
{
return Array.Empty<ISimpleStateChecker<TState>>();
}
if (array.Count == 1)
{
var jsonObject = array[0] as JsonObject;
if (jsonObject == null)
{
throw new AbpException("JSON value is not an array of objects: " + value);
}
var checker = serializer.Deserialize(jsonObject, state);
if (checker == null)
{
return Array.Empty<ISimpleStateChecker<TState>>();
}
return new[] { checker };
}
var checkers = new List<ISimpleStateChecker<TState>>();
for (var i = 0; i < array.Count; i++)
{
if (array[i] is not JsonObject jsonObject)
{
throw new AbpException("JSON value is not an array of objects: " + value);
}
checkers.Add(serializer.Deserialize(jsonObject, state));
}
return checkers.Where(x => x != null).ToArray();
}
}

1
framework/src/Volo.Abp.DistributedLocking/Volo.Abp.DistributedLocking.csproj

@ -16,6 +16,7 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.DistributedLocking.Abstractions\Volo.Abp.DistributedLocking.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.Threading\Volo.Abp.Threading.csproj" />
</ItemGroup>
<ItemGroup>

4
framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/AbpDistributedLockingModule.cs

@ -1,9 +1,11 @@
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
namespace Volo.Abp.DistributedLocking;
[DependsOn(
typeof(AbpDistributedLockingAbstractionsModule)
typeof(AbpDistributedLockingAbstractionsModule),
typeof(AbpThreadingModule)
)]
public class AbpDistributedLockingModule : AbpModule
{

16
framework/src/Volo.Abp.DistributedLocking/Volo/Abp/DistributedLocking/MedallionAbpDistributedLock.cs

@ -3,6 +3,7 @@ using System.Threading;
using System.Threading.Tasks;
using Medallion.Threading;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
namespace Volo.Abp.DistributedLocking;
@ -10,10 +11,14 @@ namespace Volo.Abp.DistributedLocking;
public class MedallionAbpDistributedLock : IAbpDistributedLock, ITransientDependency
{
protected IDistributedLockProvider DistributedLockProvider { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
public MedallionAbpDistributedLock(IDistributedLockProvider distributedLockProvider)
public MedallionAbpDistributedLock(
IDistributedLockProvider distributedLockProvider,
ICancellationTokenProvider cancellationTokenProvider)
{
DistributedLockProvider = distributedLockProvider;
CancellationTokenProvider = cancellationTokenProvider;
}
public async Task<IAbpDistributedLockHandle> TryAcquireAsync(
@ -23,7 +28,14 @@ public class MedallionAbpDistributedLock : IAbpDistributedLock, ITransientDepend
{
Check.NotNullOrWhiteSpace(name, nameof(name));
var handle = await DistributedLockProvider.TryAcquireLockAsync(name, timeout, cancellationToken);
CancellationTokenProvider.FallbackToProvider(cancellationToken);
var handle = await DistributedLockProvider.TryAcquireLockAsync(
name,
timeout,
CancellationTokenProvider.FallbackToProvider(cancellationToken)
);
if (handle == null)
{
return null;

56
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeaturesSimpleStateCheckerSerializerContributor.cs

@ -0,0 +1,56 @@
using System.Linq;
using System.Text.Json.Nodes;
using Volo.Abp.DependencyInjection;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.Features;
public class FeaturesSimpleStateCheckerSerializerContributor :
ISimpleStateCheckerSerializerContributor,
ISingletonDependency
{
public const string CheckerShortName = "F";
public string SerializeToJson<TState>(ISimpleStateChecker<TState> checker)
where TState : IHasSimpleStateCheckers<TState>
{
if (checker is not RequireFeaturesSimpleStateChecker<TState> featuresSimpleStateChecker)
{
return null;
}
var jsonObject = new JsonObject {
["T"] = CheckerShortName,
["A"] = featuresSimpleStateChecker.RequiresAll
};
var nameArray = new JsonArray();
foreach (var featureName in featuresSimpleStateChecker.FeatureNames)
{
nameArray.Add(featureName);
}
jsonObject["N"] = nameArray;
return jsonObject.ToJsonString();
}
public ISimpleStateChecker<TState> Deserialize<TState>(JsonObject jsonObject, TState state)
where TState : IHasSimpleStateCheckers<TState>
{
if (jsonObject["T"]?.ToString() != CheckerShortName)
{
return null;
}
var nameArray = jsonObject["N"] as JsonArray;
if (nameArray == null)
{
throw new AbpException("'N' is not an array in the serialized state checker! JsonObject: " + jsonObject.ToJsonString());
}
return new RequireFeaturesSimpleStateChecker<TState>(
(bool?)jsonObject["A"] ?? false,
nameArray.Select(x => x.ToString()).ToArray()
);
}
}

10
framework/src/Volo.Abp.Features/Volo/Abp/Features/RequireFeaturesSimpleStateChecker.cs

@ -7,8 +7,8 @@ namespace Volo.Abp.Features;
public class RequireFeaturesSimpleStateChecker<TState> : ISimpleStateChecker<TState>
where TState : IHasSimpleStateCheckers<TState>
{
private readonly string[] _featureNames;
private readonly bool _requiresAll;
public string[] FeatureNames { get; }
public bool RequiresAll { get; }
public RequireFeaturesSimpleStateChecker(params string[] featureNames)
: this(true, featureNames)
@ -19,13 +19,13 @@ public class RequireFeaturesSimpleStateChecker<TState> : ISimpleStateChecker<TSt
{
Check.NotNullOrEmpty(featureNames, nameof(featureNames));
_requiresAll = requiresAll;
_featureNames = featureNames;
RequiresAll = requiresAll;
FeatureNames = featureNames;
}
public async Task<bool> IsEnabledAsync(SimpleStateCheckerContext<TState> context)
{
var featureChecker = context.ServiceProvider.GetRequiredService<IFeatureChecker>();
return await featureChecker.IsEnabledAsync(_requiresAll, _featureNames);
return await featureChecker.IsEnabledAsync(RequiresAll, FeatureNames);
}
}

56
framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor.cs

@ -0,0 +1,56 @@
using System.Linq;
using System.Text.Json.Nodes;
using Volo.Abp.DependencyInjection;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.GlobalFeatures;
public class GlobalFeaturesSimpleStateCheckerSerializerContributor :
ISimpleStateCheckerSerializerContributor,
ISingletonDependency
{
public const string CheckerShortName = "G";
public string SerializeToJson<TState>(ISimpleStateChecker<TState> checker)
where TState : IHasSimpleStateCheckers<TState>
{
if (checker is not RequireGlobalFeaturesSimpleStateChecker<TState> globalFeaturesSimpleStateChecker)
{
return null;
}
var jsonObject = new JsonObject {
["T"] = CheckerShortName,
["A"] = globalFeaturesSimpleStateChecker.RequiresAll
};
var nameArray = new JsonArray();
foreach (var globalFeatureName in globalFeaturesSimpleStateChecker.GlobalFeatureNames)
{
nameArray.Add(globalFeatureName);
}
jsonObject["N"] = nameArray;
return jsonObject.ToJsonString();
}
public ISimpleStateChecker<TState> Deserialize<TState>(JsonObject jsonObject, TState state)
where TState : IHasSimpleStateCheckers<TState>
{
if (jsonObject["T"]?.ToString() != CheckerShortName)
{
return null;
}
var nameArray = jsonObject["N"] as JsonArray;
if (nameArray == null)
{
throw new AbpException("'N' is not an array in the serialized state checker! JsonObject: " + jsonObject.ToJsonString());
}
return new RequireGlobalFeaturesSimpleStateChecker<TState>(
(bool?)jsonObject["A"] ?? false,
nameArray.Select(x => x.ToString()).ToArray()
);
}
}

18
framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/RequireGlobalFeaturesSimpleStateChecker.cs

@ -8,8 +8,8 @@ namespace Volo.Abp.GlobalFeatures;
public class RequireGlobalFeaturesSimpleStateChecker<TState> : ISimpleStateChecker<TState>
where TState : IHasSimpleStateCheckers<TState>
{
private readonly string[] _globalFeatureNames;
private readonly bool _requiresAll;
public string[] GlobalFeatureNames { get; }
public bool RequiresAll { get; }
public RequireGlobalFeaturesSimpleStateChecker(params string[] globalFeatureNames)
: this(true, globalFeatureNames)
@ -20,23 +20,23 @@ public class RequireGlobalFeaturesSimpleStateChecker<TState> : ISimpleStateCheck
{
Check.NotNullOrEmpty(globalFeatureNames, nameof(globalFeatureNames));
_requiresAll = requiresAll;
_globalFeatureNames = globalFeatureNames;
RequiresAll = requiresAll;
GlobalFeatureNames = globalFeatureNames;
}
public RequireGlobalFeaturesSimpleStateChecker(bool requiresAll, params Type[] globalFeatureNames)
{
Check.NotNullOrEmpty(globalFeatureNames, nameof(globalFeatureNames));
_requiresAll = requiresAll;
_globalFeatureNames = globalFeatureNames.Select(GlobalFeatureNameAttribute.GetName).ToArray();
RequiresAll = requiresAll;
GlobalFeatureNames = globalFeatureNames.Select(GlobalFeatureNameAttribute.GetName).ToArray();
}
public Task<bool> IsEnabledAsync(SimpleStateCheckerContext<TState> context)
{
var isEnabled = _requiresAll
? _globalFeatureNames.All(x => GlobalFeatureManager.Instance.IsEnabled(x))
: _globalFeatureNames.Any(x => GlobalFeatureManager.Instance.IsEnabled(x));
var isEnabled = RequiresAll
? GlobalFeatureNames.All(x => GlobalFeatureManager.Instance.IsEnabled(x))
: GlobalFeatureNames.Any(x => GlobalFeatureManager.Instance.IsEnabled(x));
return Task.FromResult(isEnabled);
}

25
framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs

@ -12,7 +12,7 @@ public class LocalizableString : ILocalizableString
[NotNull]
public string Name { get; }
public LocalizableString(Type resourceType, [NotNull] string name)
public LocalizableString([CanBeNull] Type resourceType, [NotNull] string name)
{
Name = Check.NotNullOrEmpty(name, nameof(name));
ResourceType = resourceType;
@ -20,7 +20,28 @@ public class LocalizableString : ILocalizableString
public LocalizedString Localize(IStringLocalizerFactory stringLocalizerFactory)
{
return stringLocalizerFactory.Create(ResourceType)[Name];
var localizer = ResourceType != null
? stringLocalizerFactory.Create(ResourceType)
: stringLocalizerFactory.CreateDefaultOrNull();
if (localizer == null)
{
throw new AbpException($"Set {nameof(ResourceType)} or configure the default localization resource type (in the AbpLocalizationOptions)!");
}
var result = localizer[Name];
if (result.ResourceNotFound && ResourceType != null)
{
/* Search in the default resource if not found in the provided resource */
localizer = stringLocalizerFactory.CreateDefaultOrNull();
if (localizer != null)
{
result = localizer[Name];
}
}
return result;
}
public static LocalizableString Create<TResource>([NotNull] string name)

8
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/ILocalizableStringSerializer.cs

@ -0,0 +1,8 @@
namespace Volo.Abp.Localization;
public interface ILocalizableStringSerializer
{
string Serialize(ILocalizableString localizableString);
ILocalizableString Deserialize(string value);
}

69
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableStringSerializer.cs

@ -0,0 +1,69 @@
using System;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Localization;
public class LocalizableStringSerializer : ILocalizableStringSerializer, ITransientDependency
{
protected AbpLocalizationOptions LocalizationOptions { get; }
public LocalizableStringSerializer(IOptions<AbpLocalizationOptions> localizationOptions)
{
LocalizationOptions = localizationOptions.Value;
}
public virtual string Serialize(ILocalizableString localizableString)
{
if (localizableString is LocalizableString realLocalizableString)
{
return $"L:{LocalizationResourceNameAttribute.GetName(realLocalizableString.ResourceType)},{realLocalizableString.Name}";
}
if (localizableString is FixedLocalizableString fixedLocalizableString)
{
return $"F:{fixedLocalizableString.Value}";
}
throw new AbpException($"Unknown {nameof(ILocalizableString)} type: {localizableString.GetType().FullName}");
}
public virtual ILocalizableString Deserialize(string value)
{
if (value.IsNullOrEmpty() ||
value.Length < 3 ||
value[1] != ':')
{
return new FixedLocalizableString(value);
}
var type = value[0];
switch (type)
{
case 'F':
return new FixedLocalizableString(value.Substring(2));
case 'L':
var commaPosition = value.IndexOf(',', 2);
if (commaPosition == -1)
{
throw new AbpException("Invalid LocalizableString value: " + value);
}
var resourceName = value.Substring(2, commaPosition - 2);
var name = value.Substring(commaPosition + 1);
if (name.IsNullOrWhiteSpace())
{
throw new AbpException("Invalid LocalizableString value: " + value);
}
var resourceType = LocalizationOptions.Resources.GetOrNull(resourceName)?.ResourceType;
return new LocalizableString(
resourceType,
name
);
default:
return new FixedLocalizableString(value);
}
}
}

27
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceDictionary.cs

@ -6,6 +6,8 @@ namespace Volo.Abp.Localization;
public class LocalizationResourceDictionary : Dictionary<Type, LocalizationResource>
{
private readonly Dictionary<string, LocalizationResource> _resourcesByNames = new();
public LocalizationResource Add<TResouce>([CanBeNull] string defaultCultureName = null)
{
return Add(typeof(TResouce), defaultCultureName);
@ -18,7 +20,12 @@ public class LocalizationResourceDictionary : Dictionary<Type, LocalizationResou
throw new AbpException("This resource is already added before: " + resourceType.AssemblyQualifiedName);
}
return this[resourceType] = new LocalizationResource(resourceType, defaultCultureName);
var resource = new LocalizationResource(resourceType, defaultCultureName);
this[resourceType] = resource;
_resourcesByNames[resource.ResourceName] = resource;
return resource;
}
public LocalizationResource Get<TResource>()
@ -33,4 +40,20 @@ public class LocalizationResourceDictionary : Dictionary<Type, LocalizationResou
return resource;
}
}
public LocalizationResource Get(string resourceName)
{
var resource = GetOrNull(resourceName);
if (resource == null)
{
throw new AbpException("Can not find a resource with given name: " + resourceName);
}
return resource;
}
public LocalizationResource GetOrNull(string resourceName)
{
return _resourcesByNames.GetOrDefault(resourceName);
}
}

2
framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancySides.cs

@ -6,7 +6,7 @@ namespace Volo.Abp.MultiTenancy;
/// Represents sides in a multi tenancy application.
/// </summary>
[Flags]
public enum MultiTenancySides
public enum MultiTenancySides : byte
{
/// <summary>
/// Tenant side.

27
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/ExtraPropertyDictionaryExtensions.cs

@ -1,4 +1,5 @@
using System;
using JetBrains.Annotations;
namespace Volo.Abp.Data;
@ -26,4 +27,28 @@ public static class ExtraPropertyDictionaryExtensions
extraPropertyDictionary[key] = Enum.Parse(enumType, extraPropertyDictionary[key].ToString(), ignoreCase: true);
return extraPropertyDictionary[key];
}
}
public static bool HasSameItems(
[NotNull] this ExtraPropertyDictionary dictionary,
[NotNull] ExtraPropertyDictionary otherDictionary)
{
Check.NotNull(dictionary, nameof(dictionary));
Check.NotNull(otherDictionary, nameof(otherDictionary));
if (dictionary.Count != otherDictionary.Count)
{
return false;
}
foreach (var key in dictionary.Keys)
{
if (!otherDictionary.ContainsKey(key) ||
dictionary[key]?.ToString() != otherDictionary[key]?.ToString())
{
return false;
}
}
return true;
}
}

11
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using JetBrains.Annotations;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Reflection;
@ -121,4 +122,14 @@ public static class HasExtraPropertiesExtensions
source.RemoveProperty(property.Name);
}
}
public static bool HasSameExtraProperties(
[NotNull] this IHasExtraProperties source,
[NotNull] IHasExtraProperties other)
{
Check.NotNull(source, nameof(source));
Check.NotNull(other, nameof(other));
return source.ExtraProperties.HasSameItems(other.ExtraProperties);
}
}

2
framework/src/Volo.Abp.Threading/Volo/Abp/Threading/NullCancellationTokenProvider.cs

@ -4,7 +4,7 @@ namespace Volo.Abp.Threading;
public class NullCancellationTokenProvider : CancellationTokenProviderBase
{
public static NullCancellationTokenProvider Instance { get; } = new NullCancellationTokenProvider();
public static NullCancellationTokenProvider Instance { get; } = new();
public override CancellationToken Token => OverrideValue?.CancellationToken ?? CancellationToken.None;

23
framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingOptions_Tests.cs

@ -0,0 +1,23 @@
using Microsoft.Extensions.Options;
using Shouldly;
using Xunit;
namespace Volo.Abp.Auditing;
public class AbpAuditingOptions_Tests : AbpAuditingTestBase
{
private const string ApplicationName = "TEST_APP_NAME";
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
base.SetAbpApplicationCreationOptions(options);
options.ApplicationName = ApplicationName;
}
[Fact]
public void Should_Set_Application_Name_From_Global_Application_Name_By_Default()
{
var options = GetRequiredService<IOptions<AbpAuditingOptions>>().Value;
options.ApplicationName.ShouldBe(ApplicationName);
}
}

41
framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AuthenticatedSimpleStateCheckerSerializerContributor_Tests.cs

@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.Text.Json.Nodes;
using Shouldly;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.SimpleStateChecking;
using Xunit;
namespace Volo.Abp.Authorization;
public class AuthenticatedSimpleStateCheckerSerializerContributor_Tests
{
[Fact]
public void Should_Serialize_RequireGlobalFeaturesSimpleStateChecker()
{
var serializer = new AuthenticatedSimpleStateCheckerSerializerContributor();
var result = serializer.SerializeToJson(
new RequireAuthenticatedSimpleStateChecker<MyState>()
);
result.ShouldBe("{\"T\":\"A\"}");
}
[Fact]
public void Should_Deserialize_RequireGlobalFeaturesSimpleStateChecker()
{
var serializer = new AuthenticatedSimpleStateCheckerSerializerContributor();
var jsonObject = (JsonObject)JsonNode.Parse("{\"T\":\"A\"}");
var checker = serializer.Deserialize(jsonObject, new MyState());
checker.ShouldBeOfType<RequireAuthenticatedSimpleStateChecker<MyState>>();
var globalFeaturesSimpleStateChecker = checker as RequireAuthenticatedSimpleStateChecker<MyState>;
globalFeaturesSimpleStateChecker.ShouldNotBeNull();
}
private class MyState : IHasSimpleStateCheckers<MyState>
{
public List<ISimpleStateChecker<MyState>> StateCheckers { get; } = new();
}
}

4
framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs

@ -67,9 +67,9 @@ public class Authorization_Tests : AuthorizationTestBase
}
[Fact]
public void Should_Permission_Definition_GetGroup()
public async Task Should_Permission_Definition_GetGroup()
{
_permissionDefinitionManager.GetGroups().Count.ShouldBe(1);
(await _permissionDefinitionManager.GetGroupsAsync()).Count.ShouldBe(1);
}
[Fact]

6
framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/PermissionStateProvider_Tests.cs

@ -29,7 +29,8 @@ public class SpecifyPermissionStateProvider : PermissionStateProvider_Tests
[Fact]
public async Task PermissionState_Test()
{
var myPermission1 = PermissionDefinitionManager.Get("MyPermission1");
var myPermission1 = await PermissionDefinitionManager.GetOrNullAsync("MyPermission1");
myPermission1.ShouldNotBeNull();
myPermission1.StateCheckers.ShouldContain(x => x.GetType() == typeof(TestRequireEditionPermissionSimpleStateChecker));
(await StateCheckerManager.IsEnabledAsync(myPermission1)).ShouldBeFalse();
@ -54,7 +55,8 @@ public class GlobalPermissionStateProvider : PermissionStateProvider_Tests
[Fact]
public async Task Global_PermissionState_Test()
{
var myPermission2 = PermissionDefinitionManager.Get("MyPermission2");
var myPermission2 = await PermissionDefinitionManager.GetOrNullAsync("MyPermission2");
myPermission2.ShouldNotBeNull();
(await StateCheckerManager.IsEnabledAsync(myPermission2)).ShouldBeFalse();

37
framework/test/Volo.Abp.Core.Tests/Volo/Abp/AbpApplication_Initialize_Tests.cs

@ -1,6 +1,7 @@
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.Modularity.PlugIns;
using Xunit;
@ -142,4 +143,40 @@ public class AbpApplication_Initialize_Tests
plugInModule.OnApplicationShutdownIsCalled.ShouldBeTrue();
}
}
[Fact]
public void Should_Set_And_Get_ApplicationName()
{
const string applicationName = "MyApplication";
using (var application = AbpApplicationFactory.Create<IndependentEmptyModule>(options =>
{
options.ApplicationName = applicationName;
}))
{
application.ApplicationName.ShouldBe(applicationName);
application.Services.GetApplicationName().ShouldBe(applicationName);
application.Initialize();
application.ServiceProvider
.GetRequiredService<IApplicationNameAccessor>()
.ApplicationName
.ShouldBe(applicationName);
}
}
[Fact]
public async Task Should_Resolve_Root_Service_Provider()
{
using (var application = await AbpApplicationFactory.CreateAsync<IndependentEmptyModule>())
{
await application.InitializeAsync();
application
.ServiceProvider
.GetRequiredService<IRootServiceProvider>()
.ShouldNotBeNull();
}
}
}

46
framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeaturesSimpleStateCheckerSerializerContributor_Tests.cs

@ -0,0 +1,46 @@
using System.Collections.Generic;
using System.Text.Json.Nodes;
using Shouldly;
using Volo.Abp.SimpleStateChecking;
using Xunit;
namespace Volo.Abp.Features;
public class FeaturesSimpleStateCheckerSerializerContributor_Tests
{
[Fact]
public void Should_Serialize_RequireGlobalFeaturesSimpleStateChecker()
{
var serializer = new FeaturesSimpleStateCheckerSerializerContributor();
var result = serializer.SerializeToJson(
new RequireFeaturesSimpleStateChecker<MyState>(
"FeatureA",
"FeatureB"
)
);
result.ShouldBe("{\"T\":\"F\",\"A\":true,\"N\":[\"FeatureA\",\"FeatureB\"]}");
}
[Fact]
public void Should_Deserialize_RequireGlobalFeaturesSimpleStateChecker()
{
var serializer = new FeaturesSimpleStateCheckerSerializerContributor();
var jsonObject = (JsonObject)JsonNode.Parse("{\"T\":\"F\",\"A\":true,\"N\":[\"FeatureA\",\"FeatureB\"]}");
var checker = serializer.Deserialize<MyState>(jsonObject, new MyState());
checker.ShouldBeOfType<RequireFeaturesSimpleStateChecker<MyState>>();
var globalFeaturesSimpleStateChecker = checker as RequireFeaturesSimpleStateChecker<MyState>;
globalFeaturesSimpleStateChecker.ShouldNotBeNull();
globalFeaturesSimpleStateChecker.RequiresAll.ShouldBeTrue();
globalFeaturesSimpleStateChecker.FeatureNames[0].ShouldBe("FeatureA");
globalFeaturesSimpleStateChecker.FeatureNames[1].ShouldBe("FeatureB");
}
private class MyState : IHasSimpleStateCheckers<MyState>
{
public List<ISimpleStateChecker<MyState>> StateCheckers { get; } = new();
}
}

46
framework/test/Volo.Abp.GlobalFeatures.Tests/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor_Tests.cs

@ -0,0 +1,46 @@
using System.Collections.Generic;
using System.Text.Json.Nodes;
using Shouldly;
using Volo.Abp.SimpleStateChecking;
using Xunit;
namespace Volo.Abp.GlobalFeatures;
public class GlobalFeaturesSimpleStateCheckerSerializerContributor_Tests
{
[Fact]
public void Should_Serialize_RequireGlobalFeaturesSimpleStateChecker()
{
var serializer = new GlobalFeaturesSimpleStateCheckerSerializerContributor();
var result = serializer.SerializeToJson(
new RequireGlobalFeaturesSimpleStateChecker<MyState>(
"FeatureA",
"FeatureB"
)
);
result.ShouldBe("{\"T\":\"G\",\"A\":true,\"N\":[\"FeatureA\",\"FeatureB\"]}");
}
[Fact]
public void Should_Deserialize_RequireGlobalFeaturesSimpleStateChecker()
{
var serializer = new GlobalFeaturesSimpleStateCheckerSerializerContributor();
var jsonObject = (JsonObject)JsonNode.Parse("{\"T\":\"G\",\"A\":true,\"N\":[\"FeatureA\",\"FeatureB\"]}");
var checker = serializer.Deserialize<MyState>(jsonObject, new MyState());
checker.ShouldBeOfType<RequireGlobalFeaturesSimpleStateChecker<MyState>>();
var globalFeaturesSimpleStateChecker = checker as RequireGlobalFeaturesSimpleStateChecker<MyState>;
globalFeaturesSimpleStateChecker.ShouldNotBeNull();
globalFeaturesSimpleStateChecker.RequiresAll.ShouldBeTrue();
globalFeaturesSimpleStateChecker.GlobalFeatureNames[0].ShouldBe("FeatureA");
globalFeaturesSimpleStateChecker.GlobalFeatureNames[1].ShouldBe("FeatureB");
}
private class MyState : IHasSimpleStateCheckers<MyState>
{
public List<ISimpleStateChecker<MyState>> StateCheckers { get; } = new();
}
}

39
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalizationTestModule.cs

@ -0,0 +1,39 @@
using Volo.Abp.Localization.TestResources.Base.CountryNames;
using Volo.Abp.Localization.TestResources.Base.Validation;
using Volo.Abp.Localization.TestResources.Source;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.Localization;
[DependsOn(typeof(AbpTestBaseModule))]
[DependsOn(typeof(AbpLocalizationModule))]
public class AbpLocalizationTestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpLocalizationTestModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<LocalizationTestValidationResource>("en")
.AddVirtualJson("/Volo/Abp/Localization/TestResources/Base/Validation");
options.Resources
.Add<LocalizationTestCountryNamesResource>("en")
.AddVirtualJson("/Volo/Abp/Localization/TestResources/Base/CountryNames");
options.Resources
.Add<LocalizationTestResource>("en")
.AddVirtualJson("/Volo/Abp/Localization/TestResources/Source");
options.Resources
.Get<LocalizationTestResource>()
.AddVirtualJson("/Volo/Abp/Localization/TestResources/SourceExt");
});
}
}

45
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/AbpLocalization_Tests.cs

@ -2,17 +2,13 @@ using System.Globalization;
using System.Linq;
using Microsoft.Extensions.Localization;
using Shouldly;
using Volo.Abp.Localization.TestResources.Base.CountryNames;
using Volo.Abp.Localization.TestResources.Base.Validation;
using Volo.Abp.Localization.TestResources.Source;
using Volo.Abp.Modularity;
using Volo.Abp.Testing;
using Volo.Abp.VirtualFileSystem;
using Xunit;
namespace Volo.Abp.Localization;
public class AbpLocalization_Tests : AbpIntegratedTest<AbpLocalization_Tests.TestModule>
public class AbpLocalization_Tests : AbpIntegratedTest<AbpLocalizationTestModule>
{
private readonly IStringLocalizer<LocalizationTestResource> _localizer;
private readonly IStringLocalizerFactory _localizerFactory;
@ -76,7 +72,6 @@ public class AbpLocalization_Tests : AbpIntegratedTest<AbpLocalization_Tests.Tes
{
_localizer["Car"].Value.ShouldBe("Auto");
}
}
[Fact]
@ -106,7 +101,6 @@ public class AbpLocalization_Tests : AbpIntegratedTest<AbpLocalization_Tests.Tes
{
_localizer["SeeYou"].Value.ShouldBe("Bis bald");
}
}
[Fact]
@ -132,7 +126,6 @@ public class AbpLocalization_Tests : AbpIntegratedTest<AbpLocalization_Tests.Tes
_localizer.GetAllStrings().ShouldContain(ls => ls.Name == "USA");
}
}
[Fact]
@ -275,7 +268,6 @@ public class AbpLocalization_Tests : AbpIntegratedTest<AbpLocalization_Tests.Tes
ls.ResourceNotFound == false
);
}
}
[Fact]
@ -310,7 +302,6 @@ public class AbpLocalization_Tests : AbpIntegratedTest<AbpLocalization_Tests.Tes
ls.ResourceNotFound == false
);
}
}
[Fact]
@ -368,36 +359,4 @@ public class AbpLocalization_Tests : AbpIntegratedTest<AbpLocalization_Tests.Tes
);
}
}
[DependsOn(typeof(AbpTestBaseModule))]
[DependsOn(typeof(AbpLocalizationModule))]
public class TestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<TestModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<LocalizationTestValidationResource>("en")
.AddVirtualJson("/Volo/Abp/Localization/TestResources/Base/Validation");
options.Resources
.Add<LocalizationTestCountryNamesResource>("en")
.AddVirtualJson("/Volo/Abp/Localization/TestResources/Base/CountryNames");
options.Resources
.Add<LocalizationTestResource>("en")
.AddVirtualJson("/Volo/Abp/Localization/TestResources/Source");
options.Resources
.Get<LocalizationTestResource>()
.AddVirtualJson("/Volo/Abp/Localization/TestResources/SourceExt");
});
}
}
}
}

75
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/LocalizableStringSerializer_Tests.cs

@ -0,0 +1,75 @@
using Shouldly;
using Volo.Abp.Localization.TestResources.Source;
using Volo.Abp.Testing;
using Xunit;
namespace Volo.Abp.Localization;
public class LocalizableStringSerializer_Tests : AbpIntegratedTest<AbpLocalizationTestModule>
{
private readonly ILocalizableStringSerializer _serializer;
public LocalizableStringSerializer_Tests()
{
_serializer = GetRequiredService<ILocalizableStringSerializer>();
}
[Fact]
public void Serialize_FixedLocalizableString()
{
_serializer
.Serialize(new FixedLocalizableString(""))
.ShouldBe("F:");
_serializer
.Serialize(new FixedLocalizableString("Hello World"))
.ShouldBe("F:Hello World");
}
[Fact]
public void Serialize_LocalizableString()
{
_serializer
.Serialize(new LocalizableString(typeof(LocalizationTestResource),"Car"))
.ShouldBe("L:Test,Car");
}
[Fact]
public void Deserialize_FixedLocalizableString()
{
_serializer
.Deserialize("")
.ShouldBeOfType<FixedLocalizableString>()
.Value.ShouldBe("");
_serializer
.Deserialize("Hello")
.ShouldBeOfType<FixedLocalizableString>()
.Value.ShouldBe("Hello");
_serializer
.Deserialize("F:Hello")
.ShouldBeOfType<FixedLocalizableString>()
.Value.ShouldBe("Hello");
}
[Fact]
public void Deserialize_LocalizableString()
{
var localizableString = _serializer
.Deserialize("L:Test,Car")
.ShouldBeOfType<LocalizableString>();
localizableString.ResourceType.ShouldBe(typeof(LocalizationTestResource));
localizableString.Name.ShouldBe("Car");
Assert.Throws<AbpException>(() =>
{
_serializer.Deserialize("L:Test");
});
Assert.Throws<AbpException>(() =>
{
_serializer.Deserialize("L:Test, ");
});
}
}

1
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/LocalizationTestResource.cs

@ -7,6 +7,7 @@ namespace Volo.Abp.Localization.TestResources.Source;
typeof(LocalizationTestValidationResource),
typeof(LocalizationTestCountryNamesResource)
)]
[LocalizationResourceName("Test")]
public sealed class LocalizationTestResource
{

21
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HasExtraPropertiesExtensions_Tests.cs

@ -45,4 +45,25 @@ public class HasExtraPropertiesExtensions_Tests
city.SetProperty("DateTime?", DateTime.MinValue);
city.GetProperty<DateTime?>("DateTime?").ShouldBe(DateTime.MinValue);
}
[Fact]
public void HasSameExtraProperties_Tests()
{
var adana = new City(Guid.NewGuid(), "Adana");
adana.SetProperty("IsHot", true);
var antalya = new City(Guid.NewGuid(), "Antalya");
antalya.SetProperty("IsHot", true);
adana.HasSameExtraProperties(antalya).ShouldBeTrue();
adana.SetProperty("IsHot", false);
adana.HasSameExtraProperties(antalya).ShouldBeFalse();
adana.SetProperty("IsHot", true);
adana.HasSameExtraProperties(antalya).ShouldBeTrue();
adana.SetProperty("Population", 3_000_000);
adana.HasSameExtraProperties(antalya).ShouldBeFalse();
}
}

3
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeaturePermissionDefinitionProvider.cs

@ -11,8 +11,7 @@ public class FeaturePermissionDefinitionProvider : PermissionDefinitionProvider
{
var featureManagementGroup = context.AddGroup(
FeatureManagementPermissions.GroupName,
L("Permission:FeatureManagement"),
multiTenancySide: MultiTenancySides.Host);
L("Permission:FeatureManagement"));
featureManagementGroup.AddPermission(
FeatureManagementPermissions.ManageHostFeatures,

4
modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/PermissionGrantInfoDto.cs

@ -7,6 +7,10 @@ public class PermissionGrantInfoDto
public string Name { get; set; }
public string DisplayName { get; set; }
public string DisplayNameKey { get; set; }
public string DisplayNameResource { get; set; }
public string ParentName { get; set; }

4
modules/permission-management/src/Volo.Abp.PermissionManagement.Application.Contracts/Volo/Abp/PermissionManagement/PermissionGroupDto.cs

@ -7,6 +7,10 @@ public class PermissionGroupDto
public string Name { get; set; }
public string DisplayName { get; set; }
public string DisplayNameKey { get; set; }
public string DisplayNameResource { get; set; }
public List<PermissionGrantInfoDto> Permissions { get; set; }
}

54
modules/permission-management/src/Volo.Abp.PermissionManagement.Application/Volo/Abp/PermissionManagement/PermissionAppService.cs

@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Volo.Abp.Application.Services;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy;
using Volo.Abp.SimpleStateChecking;
@ -43,14 +44,9 @@ public class PermissionAppService : ApplicationService, IPermissionAppService
var multiTenancySide = CurrentTenant.GetMultiTenancySide();
foreach (var group in PermissionDefinitionManager.GetGroups())
foreach (var group in await PermissionDefinitionManager.GetGroupsAsync())
{
var groupDto = new PermissionGroupDto
{
Name = group.Name,
DisplayName = group.DisplayName.Localize(StringLocalizerFactory),
Permissions = new List<PermissionGrantInfoDto>()
};
var groupDto = CreatePermissionGroupDto(group);
var neededCheckPermissions = new List<PermissionDefinition>();
@ -70,14 +66,9 @@ public class PermissionAppService : ApplicationService, IPermissionAppService
continue;
}
var grantInfoDtos = neededCheckPermissions.Select(x => new PermissionGrantInfoDto
{
Name = x.Name,
DisplayName = x.DisplayName.Localize(StringLocalizerFactory),
ParentName = x.Parent?.Name,
AllowedProviders = x.Providers,
GrantedProviders = new List<ProviderInfoDto>()
}).ToList();
var grantInfoDtos = neededCheckPermissions
.Select(CreatePermissionGrantInfoDto)
.ToList();
var multipleGrantInfo = await PermissionManager.GetAsync(neededCheckPermissions.Select(x => x.Name).ToArray(), providerName, providerKey);
@ -108,6 +99,39 @@ public class PermissionAppService : ApplicationService, IPermissionAppService
return result;
}
private PermissionGrantInfoDto CreatePermissionGrantInfoDto(PermissionDefinition permission)
{
var localizableDisplayName = permission.DisplayName as LocalizableString;
return new PermissionGrantInfoDto {
Name = permission.Name,
DisplayName = permission.DisplayName.Localize(StringLocalizerFactory),
DisplayNameKey = localizableDisplayName?.Name,
DisplayNameResource = localizableDisplayName?.ResourceType != null
? LocalizationResourceNameAttribute.GetName(localizableDisplayName.ResourceType)
: null,
ParentName = permission.Parent?.Name,
AllowedProviders = permission.Providers,
GrantedProviders = new List<ProviderInfoDto>()
};
}
private PermissionGroupDto CreatePermissionGroupDto(PermissionGroupDefinition group)
{
var localizableDisplayName = group.DisplayName as LocalizableString;
return new PermissionGroupDto
{
Name = group.Name,
DisplayName = group.DisplayName.Localize(StringLocalizerFactory),
DisplayNameKey = localizableDisplayName?.Name,
DisplayNameResource = localizableDisplayName?.ResourceType != null
? LocalizationResourceNameAttribute.GetName(localizableDisplayName.ResourceType)
: null,
Permissions = new List<PermissionGrantInfoDto>()
};
}
public virtual async Task UpdateAsync(string providerName, string providerKey, UpdatePermissionsDto input)
{
await CheckProviderPolicy(providerName);

49
modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor.cs

@ -4,7 +4,9 @@ using System.Linq;
using System.Threading.Tasks;
using Blazorise;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Components.Web.Configuration;
using Volo.Abp.Localization;
using Volo.Abp.PermissionManagement.Localization;
namespace Volo.Abp.PermissionManagement.Blazor.Components;
@ -14,6 +16,8 @@ public partial class PermissionManagementModal
[Inject] protected IPermissionAppService PermissionAppService { get; set; }
[Inject] protected ICurrentApplicationConfigurationCacheResetService CurrentApplicationConfigurationCacheResetService { get; set; }
[Inject] protected IOptions<AbpLocalizationOptions> LocalizationOptions { get; set; }
protected Modal _modal;
protected string _providerName;
@ -80,6 +84,8 @@ public partial class PermissionManagementModal
var result = await PermissionAppService.GetAsync(_providerName, _providerKey);
UpdateLocalizations(result);
_entityDisplayName = entityDisplayName ?? result.EntityDisplayName;
_groups = result.Groups;
@ -246,4 +252,47 @@ public partial class PermissionManagementModal
eventArgs.Cancel = eventArgs.CloseReason == CloseReason.FocusLostClosing;
return Task.CompletedTask;
}
protected virtual void UpdateLocalizations(GetPermissionListResultDto result)
{
foreach (var group in result.Groups)
{
group.DisplayName = Localize(
group.DisplayNameKey,
group.DisplayNameResource,
group.DisplayName
);
foreach (var permission in group.Permissions)
{
permission.DisplayName = Localize(
permission.DisplayNameKey,
permission.DisplayNameResource,
permission.DisplayName
);
}
}
}
protected virtual string Localize(string key, string resourceName, string fallbackValue)
{
if (key.IsNullOrEmpty() || resourceName.IsNullOrEmpty())
{
return fallbackValue;
}
var resource = LocalizationOptions.Value.Resources.GetOrNull(resourceName);
if (resource == null)
{
return fallbackValue;
}
var result = new LocalizableString(resource.ResourceType, key).Localize(StringLocalizerFactory);
if (result.ResourceNotFound)
{
return fallbackValue;
}
return result.Value;
}
}

15
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionDefinitionRecordConsts.cs

@ -0,0 +1,15 @@
namespace Volo.Abp.PermissionManagement;
public class PermissionDefinitionRecordConsts
{
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxNameLength { get; set; } = 128;
public static int MaxDisplayNameLength { get; set; } = 256;
public static int MaxProvidersLength { get; set; } = 128;
public static int MaxStateCheckersLength { get; set; } = 256;
}

5
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionGrantConsts.cs

@ -2,11 +2,6 @@
public static class PermissionGrantConsts
{
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxNameLength { get; set; } = 128;
/// <summary>
/// Default value: 64
/// </summary>

11
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/PermissionGroupDefinitionRecordConsts.cs

@ -0,0 +1,11 @@
namespace Volo.Abp.PermissionManagement;
public class PermissionGroupDefinitionRecordConsts
{
/// <summary>
/// Default value: 128
/// </summary>
public static int MaxNameLength { get; set; } = 128;
public static int MaxDisplayNameLength { get; set; } = 256;
}

4
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo.Abp.PermissionManagement.Domain.csproj

@ -23,4 +23,8 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Json\Volo.Abp.Json.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Polly" Version="$(PollyPackageVersion)" />
</ItemGroup>
</Project>

131
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs

@ -1,11 +1,19 @@
using System.Collections.Generic;
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Polly;
using Volo.Abp.Authorization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
namespace Volo.Abp.PermissionManagement;
@ -16,5 +24,124 @@ namespace Volo.Abp.PermissionManagement;
[DependsOn(typeof(AbpJsonModule))]
public class AbpPermissionManagementDomainModule : AbpModule
{
private readonly CancellationTokenSource _cancellationTokenSource = new();
public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
{
InitializeDynamicPermissions(context);
return Task.CompletedTask;
}
}
public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context)
{
_cancellationTokenSource.Cancel();
return Task.CompletedTask;
}
private void InitializeDynamicPermissions(ApplicationInitializationContext context)
{
var options = context
.ServiceProvider
.GetRequiredService<IOptions<PermissionManagementOptions>>()
.Value;
if (!options.SaveStaticPermissionsToDatabase && !options.IsDynamicPermissionStoreEnabled)
{
return;
}
var rootServiceProvider = context.ServiceProvider.GetRequiredService<IRootServiceProvider>();
Task.Run(async () =>
{
using var scope = rootServiceProvider.CreateScope();
var applicationLifetime = scope.ServiceProvider.GetService<IHostApplicationLifetime>();
var cancellationTokenProvider = scope.ServiceProvider.GetRequiredService<ICancellationTokenProvider>();
var cancellationToken = applicationLifetime?.ApplicationStopping ?? _cancellationTokenSource.Token;
try
{
using (cancellationTokenProvider.Use(cancellationToken))
{
if (cancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await SaveStaticPermissionsToDatabaseAsync(options, scope, cancellationTokenProvider);
if (cancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await PreCacheDynamicPermissionsAsync(options, scope);
}
}
// ReSharper disable once EmptyGeneralCatchClause (No need to log since it is logged above)
catch { }
});
}
private async static Task SaveStaticPermissionsToDatabaseAsync(
PermissionManagementOptions options,
IServiceScope scope,
ICancellationTokenProvider cancellationTokenProvider)
{
if (!options.SaveStaticPermissionsToDatabase)
{
return;
}
await Policy
.Handle<Exception>()
.WaitAndRetryAsync(8, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt) * 10))
.ExecuteAsync(async _ =>
{
try
{
// ReSharper disable once AccessToDisposedClosure
await scope
.ServiceProvider
.GetRequiredService<IStaticPermissionSaver>()
.SaveAsync();
}
catch (Exception ex)
{
// ReSharper disable once AccessToDisposedClosure
scope.ServiceProvider
.GetService<ILogger<AbpPermissionManagementDomainModule>>()?
.LogException(ex);
throw; // Polly will catch it
}
}, cancellationTokenProvider.Token);
}
private async static Task PreCacheDynamicPermissionsAsync(PermissionManagementOptions options, IServiceScope scope)
{
if (!options.IsDynamicPermissionStoreEnabled)
{
return;
}
try
{
// Pre-cache permissions, so first request doesn't wait
await scope
.ServiceProvider
.GetRequiredService<IDynamicPermissionDefinitionStore>()
.GetGroupsAsync();
}
catch (Exception ex)
{
// ReSharper disable once AccessToDisposedClosure
scope
.ServiceProvider
.GetService<ILogger<AbpPermissionManagementDomainModule>>()?
.LogException(ex);
throw; // It will be cached in InitializeDynamicPermissions
}
}
}

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

@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Threading;
namespace Volo.Abp.PermissionManagement;
[Dependency(ReplaceServices = true)]
public class DynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStore, ITransientDependency
{
protected IPermissionGroupDefinitionRecordRepository PermissionGroupRepository { get; }
protected IPermissionDefinitionRecordRepository PermissionRepository { get; }
protected IPermissionDefinitionSerializer PermissionDefinitionSerializer { get; }
protected IDynamicPermissionDefinitionStoreInMemoryCache StoreCache { get; }
protected IDistributedCache DistributedCache { get; }
protected IAbpDistributedLock DistributedLock { get; }
public PermissionManagementOptions PermissionManagementOptions { get; }
protected AbpDistributedCacheOptions CacheOptions { get; }
public DynamicPermissionDefinitionStore(
IPermissionGroupDefinitionRecordRepository permissionGroupRepository,
IPermissionDefinitionRecordRepository permissionRepository,
IPermissionDefinitionSerializer permissionDefinitionSerializer,
IDynamicPermissionDefinitionStoreInMemoryCache storeCache,
IDistributedCache distributedCache,
IOptions<AbpDistributedCacheOptions> cacheOptions,
IOptions<PermissionManagementOptions> permissionManagementOptions,
IAbpDistributedLock distributedLock)
{
PermissionGroupRepository = permissionGroupRepository;
PermissionRepository = permissionRepository;
PermissionDefinitionSerializer = permissionDefinitionSerializer;
StoreCache = storeCache;
DistributedCache = distributedCache;
DistributedLock = distributedLock;
PermissionManagementOptions = permissionManagementOptions.Value;
CacheOptions = cacheOptions.Value;
}
public virtual async Task<PermissionDefinition> GetOrNullAsync(string name)
{
if (!PermissionManagementOptions.IsDynamicPermissionStoreEnabled)
{
return null;
}
using (await StoreCache.SyncSemaphore.LockAsync())
{
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetPermissionOrNull(name);
}
}
public virtual async Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync()
{
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()
{
if (!PermissionManagementOptions.IsDynamicPermissionStoreEnabled)
{
return Array.Empty<PermissionGroupDefinition>();
}
using (await StoreCache.SyncSemaphore.LockAsync())
{
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetGroups().ToImmutableList();
}
}
protected virtual async Task EnsureCacheIsUptoDateAsync()
{
if (StoreCache.LastCheckTime.HasValue &&
DateTime.Now.Subtract(StoreCache.LastCheckTime.Value).TotalSeconds < 30)
{
/* We get the latest permission with a small delay for optimization */
return;
}
var stampInDistributedCache = await GetOrSetStampInDistributedCache();
if (stampInDistributedCache == StoreCache.CacheStamp)
{
StoreCache.LastCheckTime = DateTime.Now;
return;
}
await UpdateInMemoryStoreCache();
StoreCache.CacheStamp = stampInDistributedCache;
StoreCache.LastCheckTime = DateTime.Now;
}
protected virtual async Task UpdateInMemoryStoreCache()
{
var permissionGroupRecords = await PermissionGroupRepository.GetListAsync();
var permissionRecords = await PermissionRepository.GetListAsync();
await StoreCache.FillAsync(permissionGroupRecords, permissionRecords);
}
protected virtual async Task<string> GetOrSetStampInDistributedCache()
{
var cacheKey = GetCommonStampCacheKey();
var stampInDistributedCache = await DistributedCache.GetStringAsync(cacheKey);
if (stampInDistributedCache != null)
{
return stampInDistributedCache;
}
await using (var commonLockHandle = await DistributedLock
.TryAcquireAsync(GetCommonDistributedLockKey(), TimeSpan.FromMinutes(2)))
{
if (commonLockHandle == null)
{
/* This request will fail */
throw new AbpException(
"Could not acquire distributed lock for permission definition common stamp check!"
);
}
stampInDistributedCache = await DistributedCache.GetStringAsync(cacheKey);
if (stampInDistributedCache != null)
{
return stampInDistributedCache;
}
stampInDistributedCache = Guid.NewGuid().ToString();
await DistributedCache.SetStringAsync(
cacheKey,
stampInDistributedCache,
new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromDays(30) //TODO: Make it configurable?
}
);
}
return stampInDistributedCache;
}
protected virtual string GetCommonStampCacheKey()
{
return $"{CacheOptions.KeyPrefix}_AbpInMemoryPermissionCacheStamp";
}
protected virtual string GetCommonDistributedLockKey()
{
return $"{CacheOptions.KeyPrefix}_Common_AbpPermissionUpdateLock";
}
}

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

@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.PermissionManagement;
public class DynamicPermissionDefinitionStoreInMemoryCache :
IDynamicPermissionDefinitionStoreInMemoryCache,
ISingletonDependency
{
public string CacheStamp { get; set; }
protected IDictionary<string, PermissionGroupDefinition> PermissionGroupDefinitions { get; }
protected IDictionary<string, PermissionDefinition> PermissionDefinitions { get; }
protected ISimpleStateCheckerSerializer StateCheckerSerializer { get; }
protected ILocalizableStringSerializer LocalizableStringSerializer { get; }
public SemaphoreSlim SyncSemaphore { get; } = new(1, 1);
public DateTime? LastCheckTime { get; set; }
public DynamicPermissionDefinitionStoreInMemoryCache(
ISimpleStateCheckerSerializer stateCheckerSerializer,
ILocalizableStringSerializer localizableStringSerializer)
{
StateCheckerSerializer = stateCheckerSerializer;
LocalizableStringSerializer = localizableStringSerializer;
PermissionGroupDefinitions = new Dictionary<string, PermissionGroupDefinition>();
PermissionDefinitions = new Dictionary<string, PermissionDefinition>();
}
public Task FillAsync(
List<PermissionGroupDefinitionRecord> permissionGroupRecords,
List<PermissionDefinitionRecord> permissionRecords)
{
PermissionGroupDefinitions.Clear();
PermissionDefinitions.Clear();
var context = new PermissionDefinitionContext(null);
foreach (var permissionGroupRecord in permissionGroupRecords)
{
var permissionGroup = context.AddGroup(
permissionGroupRecord.Name,
LocalizableStringSerializer.Deserialize(permissionGroupRecord.DisplayName)
);
PermissionGroupDefinitions[permissionGroup.Name] = permissionGroup;
foreach (var property in permissionGroupRecord.ExtraProperties)
{
permissionGroup[property.Key] = property.Value;
}
var permissionRecordsInThisGroup = permissionRecords
.Where(p => p.GroupName == permissionGroup.Name);
foreach (var permissionRecord in permissionRecordsInThisGroup.Where(x => x.ParentName == null))
{
AddPermissionRecursively(permissionGroup, permissionRecord, permissionRecords);
}
}
return Task.CompletedTask;
}
public PermissionDefinition GetPermissionOrNull(string name)
{
return PermissionDefinitions.GetOrDefault(name);
}
public IReadOnlyList<PermissionDefinition> GetPermissions()
{
return PermissionDefinitions.Values.ToList();
}
public IReadOnlyList<PermissionGroupDefinition> GetGroups()
{
return PermissionGroupDefinitions.Values.ToList();
}
private void AddPermissionRecursively(ICanAddChildPermission permissionContainer,
PermissionDefinitionRecord permissionRecord,
List<PermissionDefinitionRecord> allPermissionRecords)
{
var permission = permissionContainer.AddPermission(
permissionRecord.Name,
LocalizableStringSerializer.Deserialize(permissionRecord.DisplayName),
permissionRecord.MultiTenancySide,
permissionRecord.IsEnabled
);
PermissionDefinitions[permission.Name] = permission;
if (!permissionRecord.Providers.IsNullOrWhiteSpace())
{
permission.Providers.AddRange(permissionRecord.Providers.Split(','));
}
if (!permissionRecord.StateCheckers.IsNullOrWhiteSpace())
{
var checkers = StateCheckerSerializer
.DeserializeArray(
permissionRecord.StateCheckers,
permission
);
permission.StateCheckers.AddRange(checkers);
}
foreach (var property in permissionRecord.ExtraProperties)
{
permission[property.Key] = property.Value;
}
foreach (var subPermission in allPermissionRecords.Where(p => p.ParentName == permissionRecord.Name))
{
AddPermissionRecursively(permission, subPermission, allPermissionRecords);
}
}
}

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

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Authorization.Permissions;
namespace Volo.Abp.PermissionManagement;
public interface IDynamicPermissionDefinitionStoreInMemoryCache
{
string CacheStamp { get; set; }
SemaphoreSlim SyncSemaphore { get; }
DateTime? LastCheckTime { get; set; }
Task FillAsync(
List<PermissionGroupDefinitionRecord> permissionGroupRecords,
List<PermissionDefinitionRecord> permissionRecords);
PermissionDefinition GetPermissionOrNull(string name);
IReadOnlyList<PermissionDefinition> GetPermissions();
IReadOnlyList<PermissionGroupDefinition> GetGroups();
}

13
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionDefinitionRecordRepository.cs

@ -0,0 +1,13 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace Volo.Abp.PermissionManagement;
public interface IPermissionDefinitionRecordRepository : IBasicRepository<PermissionDefinitionRecord, Guid>
{
Task<PermissionDefinitionRecord> FindByNameAsync(
string name,
CancellationToken cancellationToken = default);
}

19
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionDefinitionSerializer.cs

@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.Abp.Authorization.Permissions;
namespace Volo.Abp.PermissionManagement;
public interface IPermissionDefinitionSerializer
{
Task<(PermissionGroupDefinitionRecord[], PermissionDefinitionRecord[])>
SerializeAsync(IEnumerable<PermissionGroupDefinition> permissionGroups);
Task<PermissionGroupDefinitionRecord> SerializeAsync(
PermissionGroupDefinition permissionGroup);
Task<PermissionDefinitionRecord> SerializeAsync(
PermissionDefinition permission,
[CanBeNull] PermissionGroupDefinition permissionGroup);
}

9
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IPermissionGroupDefinitionRecordRepository.cs

@ -0,0 +1,9 @@
using System;
using Volo.Abp.Domain.Repositories;
namespace Volo.Abp.PermissionManagement;
public interface IPermissionGroupDefinitionRecordRepository : IBasicRepository<PermissionGroupDefinitionRecord, Guid>
{
}

8
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/IStaticPermissionSaver.cs

@ -0,0 +1,8 @@
using System.Threading.Tasks;
namespace Volo.Abp.PermissionManagement;
public interface IStaticPermissionSaver
{
Task SaveAsync();
}

7
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDataSeedContributor.cs

@ -23,17 +23,16 @@ public class PermissionDataSeedContributor : IDataSeedContributor, ITransientDep
CurrentTenant = currentTenant;
}
public virtual Task SeedAsync(DataSeedContext context)
public virtual async Task SeedAsync(DataSeedContext context)
{
var multiTenancySide = CurrentTenant.GetMultiTenancySide();
var permissionNames = PermissionDefinitionManager
.GetPermissions()
var permissionNames = (await PermissionDefinitionManager.GetPermissionsAsync())
.Where(p => p.MultiTenancySide.HasFlag(multiTenancySide))
.Where(p => !p.Providers.Any() || p.Providers.Contains(RolePermissionValueProvider.ProviderName))
.Select(p => p.Name)
.ToArray();
return PermissionDataSeeder.SeedAsync(
await PermissionDataSeeder.SeedAsync(
RolePermissionValueProvider.ProviderName,
"admin",
permissionNames,

175
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionRecord.cs

@ -0,0 +1,175 @@
using System;
using System.Text.Json.Serialization;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.PermissionManagement;
public class PermissionDefinitionRecord : BasicAggregateRoot<Guid>, IHasExtraProperties
{
/* Ignoring Id because it is different whenever we create an instance of
* this class, and we are using Json Serialize, than Hash to understand
* if permission definitions have changed (in StaticPermissionSaver.CalculateHash()).
*/
[JsonIgnore]
public override Guid Id { get; protected set; }
public string GroupName { get; set; }
public string Name { get; set; }
public string ParentName { get; set; }
public string DisplayName { get; set; }
public bool IsEnabled { get; set; }
public MultiTenancySides MultiTenancySide { get; set; }
/// <summary>
/// Comma separated list of provider names.
/// </summary>
public string Providers { get; set; }
/// <summary>
/// Serialized string to store info about the state checkers.
/// </summary>
public string StateCheckers { get; set; }
public ExtraPropertyDictionary ExtraProperties { get; protected set; }
public PermissionDefinitionRecord()
{
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public PermissionDefinitionRecord(
Guid id,
string groupName,
string name,
string parentName,
string displayName,
bool isEnabled = true,
MultiTenancySides multiTenancySide = MultiTenancySides.Both,
string providers = null,
string stateCheckers = null)
: base(id)
{
GroupName = Check.NotNullOrWhiteSpace(groupName, nameof(groupName), PermissionGroupDefinitionRecordConsts.MaxNameLength);
Name = Check.NotNullOrWhiteSpace(name, nameof(name), PermissionDefinitionRecordConsts.MaxNameLength);
ParentName = Check.Length(parentName, nameof(parentName), PermissionDefinitionRecordConsts.MaxNameLength);
DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName), PermissionDefinitionRecordConsts.MaxDisplayNameLength);
IsEnabled = isEnabled;
MultiTenancySide = multiTenancySide;
Providers = providers;
StateCheckers = stateCheckers;
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public bool HasSameData(PermissionDefinitionRecord otherRecord)
{
if (Name != otherRecord.Name)
{
return false;
}
if (GroupName != otherRecord.GroupName)
{
return false;
}
if (ParentName != otherRecord.ParentName)
{
return false;
}
if (DisplayName != otherRecord.DisplayName)
{
return false;
}
if (IsEnabled != otherRecord.IsEnabled)
{
return false;
}
if (MultiTenancySide != otherRecord.MultiTenancySide)
{
return false;
}
if (Providers != otherRecord.Providers)
{
return false;
}
if (StateCheckers != otherRecord.StateCheckers)
{
return false;
}
if (!this.HasSameExtraProperties(otherRecord))
{
return false;
}
return true;
}
public void Patch(PermissionDefinitionRecord otherRecord)
{
if (Name != otherRecord.Name)
{
Name = otherRecord.Name;
}
if (GroupName != otherRecord.GroupName)
{
GroupName = otherRecord.GroupName;
}
if (ParentName != otherRecord.ParentName)
{
ParentName = otherRecord.ParentName;
}
if (DisplayName != otherRecord.DisplayName)
{
DisplayName = otherRecord.DisplayName;
}
if (IsEnabled != otherRecord.IsEnabled)
{
IsEnabled = otherRecord.IsEnabled;
}
if (MultiTenancySide != otherRecord.MultiTenancySide)
{
MultiTenancySide = otherRecord.MultiTenancySide;
}
if (Providers != otherRecord.Providers)
{
Providers = otherRecord.Providers;
}
if (StateCheckers != otherRecord.StateCheckers)
{
StateCheckers = otherRecord.StateCheckers;
}
if (!this.HasSameExtraProperties(otherRecord))
{
this.ExtraProperties.Clear();
foreach (var property in otherRecord.ExtraProperties)
{
this.ExtraProperties.Add(property.Key, property.Value);
}
}
}
}

106
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer.cs

@ -0,0 +1,106 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Localization;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.PermissionManagement;
public class PermissionDefinitionSerializer : IPermissionDefinitionSerializer, ITransientDependency
{
protected ISimpleStateCheckerSerializer StateCheckerSerializer { get; }
protected IGuidGenerator GuidGenerator { get; }
protected ILocalizableStringSerializer LocalizableStringSerializer { get; }
public PermissionDefinitionSerializer(
IGuidGenerator guidGenerator,
ISimpleStateCheckerSerializer stateCheckerSerializer,
ILocalizableStringSerializer localizableStringSerializer)
{
StateCheckerSerializer = stateCheckerSerializer;
LocalizableStringSerializer = localizableStringSerializer;
GuidGenerator = guidGenerator;
}
public async Task<(PermissionGroupDefinitionRecord[], PermissionDefinitionRecord[])>
SerializeAsync(IEnumerable<PermissionGroupDefinition> permissionGroups)
{
var permissionGroupRecords = new List<PermissionGroupDefinitionRecord>();
var permissionRecords = new List<PermissionDefinitionRecord>();
foreach (var permissionGroup in permissionGroups)
{
permissionGroupRecords.Add(await SerializeAsync(permissionGroup));
foreach (var permission in permissionGroup.GetPermissionsWithChildren())
{
permissionRecords.Add(await SerializeAsync(permission, permissionGroup));
}
}
return (permissionGroupRecords.ToArray(), permissionRecords.ToArray());
}
public Task<PermissionGroupDefinitionRecord> SerializeAsync(PermissionGroupDefinition permissionGroup)
{
using (CultureHelper.Use(CultureInfo.InvariantCulture))
{
var permissionGroupRecord = new PermissionGroupDefinitionRecord(
GuidGenerator.Create(),
permissionGroup.Name,
LocalizableStringSerializer.Serialize(permissionGroup.DisplayName)
);
foreach (var property in permissionGroup.Properties)
{
permissionGroupRecord.SetProperty(property.Key, property.Value);
}
return Task.FromResult(permissionGroupRecord);
}
}
public Task<PermissionDefinitionRecord> SerializeAsync(
PermissionDefinition permission,
PermissionGroupDefinition permissionGroup)
{
using (CultureHelper.Use(CultureInfo.InvariantCulture))
{
var permissionRecord = new PermissionDefinitionRecord(
GuidGenerator.Create(),
permissionGroup?.Name,
permission.Name,
permission.Parent?.Name,
LocalizableStringSerializer.Serialize(permission.DisplayName),
permission.IsEnabled,
permission.MultiTenancySide,
SerializeProviders(permission.Providers),
SerializeStateCheckers(permission.StateCheckers)
);
foreach (var property in permission.Properties)
{
permissionRecord.SetProperty(property.Key, property.Value);
}
return Task.FromResult(permissionRecord);
}
}
protected virtual string SerializeProviders(ICollection<string> providers)
{
return providers.Any()
? providers.JoinAsString(",")
: null;
}
protected virtual string SerializeStateCheckers(List<ISimpleStateChecker<PermissionDefinition>> stateCheckers)
{
return StateCheckerSerializer.Serialize(stateCheckers);
}
}

84
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionGroupDefinitionRecord.cs

@ -0,0 +1,84 @@
using System;
using System.Text.Json.Serialization;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.PermissionManagement;
public class PermissionGroupDefinitionRecord : BasicAggregateRoot<Guid>, IHasExtraProperties
{
/* Ignoring Id because it is different whenever we create an instance of
* this class, and we are using Json Serialize, than Hash to understand
* if permission definitions have changed (in StaticPermissionSaver.CalculateHash()).
*/
[JsonIgnore]
public override Guid Id { get; protected set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public ExtraPropertyDictionary ExtraProperties { get; protected set; }
public PermissionGroupDefinitionRecord()
{
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public PermissionGroupDefinitionRecord(
Guid id,
string name,
string displayName)
: base(id)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name), PermissionGroupDefinitionRecordConsts.MaxNameLength);
DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName), PermissionGroupDefinitionRecordConsts.MaxDisplayNameLength);;
ExtraProperties = new ExtraPropertyDictionary();
this.SetDefaultsForExtraProperties();
}
public bool HasSameData(PermissionGroupDefinitionRecord otherRecord)
{
if (Name != otherRecord.Name)
{
return false;
}
if (DisplayName != otherRecord.DisplayName)
{
return false;
}
if (!this.HasSameExtraProperties(otherRecord))
{
return false;
}
return true;
}
public void Patch(PermissionGroupDefinitionRecord otherRecord)
{
if (Name != otherRecord.Name)
{
Name = otherRecord.Name;
}
if (DisplayName != otherRecord.DisplayName)
{
DisplayName = otherRecord.DisplayName;
}
if (!this.HasSameExtraProperties(otherRecord))
{
this.ExtraProperties.Clear();
foreach (var property in otherRecord.ExtraProperties)
{
this.ExtraProperties.Add(property.Key, property.Value);
}
}
}
}

17
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManagementOptions.cs

@ -8,10 +8,27 @@ public class PermissionManagementOptions
public ITypeList<IPermissionManagementProvider> ManagementProviders { get; }
public Dictionary<string, string> ProviderPolicies { get; }
public HashSet<string> DeletedPermissions { get; }
public HashSet<string> DeletedPermissionGroups { get; }
/// <summary>
/// Default: true.
/// </summary>
public bool SaveStaticPermissionsToDatabase { get; set; } = true;
/// <summary>
/// Default: false.
/// </summary>
public bool IsDynamicPermissionStoreEnabled { get; set; }
public PermissionManagementOptions()
{
ManagementProviders = new TypeList<IPermissionManagementProvider>();
ProviderPolicies = new Dictionary<string, string>();
DeletedPermissions = new HashSet<string>();
DeletedPermissionGroups = new HashSet<string>();
}
}

78
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionManager.cs

@ -62,18 +62,62 @@ public class PermissionManager : IPermissionManager, ISingletonDependency
public virtual async Task<PermissionWithGrantedProviders> GetAsync(string permissionName, string providerName, string providerKey)
{
return await GetInternalAsync(PermissionDefinitionManager.Get(permissionName), providerName, providerKey);
var permission = await PermissionDefinitionManager.GetOrNullAsync(permissionName);
if (permission == null)
{
return new PermissionWithGrantedProviders(permissionName, false);
}
return await GetInternalAsync(
permission,
providerName,
providerKey
);
}
public virtual async Task<MultiplePermissionWithGrantedProviders> GetAsync(string[] permissionNames, string providerName, string providerKey)
public virtual async Task<MultiplePermissionWithGrantedProviders> GetAsync(
string[] permissionNames,
string providerName,
string providerKey)
{
var permissionDefinitions = permissionNames.Select(x => PermissionDefinitionManager.Get(x)).ToArray();
return await GetInternalAsync(permissionDefinitions, providerName, providerKey);
var permissions = new List<PermissionDefinition>();
var undefinedPermissions = new List<string>();
foreach (var permissionName in permissionNames)
{
var permission = await PermissionDefinitionManager.GetOrNullAsync(permissionName);
if (permission != null)
{
permissions.Add(permission);
}
else
{
undefinedPermissions.Add(permissionName);
}
}
if (!permissions.Any())
{
return new MultiplePermissionWithGrantedProviders(undefinedPermissions.ToArray());
}
var result = await GetInternalAsync(
permissions.ToArray(),
providerName,
providerKey
);
foreach (var undefinedPermission in undefinedPermissions)
{
result.Result.Add(new PermissionWithGrantedProviders(undefinedPermission, false));
}
return result;
}
public virtual async Task<List<PermissionWithGrantedProviders>> GetAllAsync(string providerName, string providerKey)
{
var permissionDefinitions = PermissionDefinitionManager.GetPermissions().ToArray();
var permissionDefinitions = (await PermissionDefinitionManager.GetPermissionsAsync()).ToArray();
var multiplePermissionWithGrantedProviders = await GetInternalAsync(permissionDefinitions, providerName, providerKey);
@ -83,7 +127,13 @@ public class PermissionManager : IPermissionManager, ISingletonDependency
public virtual async Task SetAsync(string permissionName, string providerName, string providerKey, bool isGranted)
{
var permission = PermissionDefinitionManager.Get(permissionName);
var permission = await PermissionDefinitionManager.GetOrNullAsync(permissionName);
if (permission == null)
{
/* Silently ignore undefined permissions,
maybe they were removed from dynamic permission definition store */
return;
}
if (!permission.IsEnabled || !await SimpleStateCheckerManager.IsEnabledAsync(permission))
{
@ -146,14 +196,24 @@ public class PermissionManager : IPermissionManager, ISingletonDependency
}
}
protected virtual async Task<PermissionWithGrantedProviders> GetInternalAsync(PermissionDefinition permission, string providerName, string providerKey)
protected virtual async Task<PermissionWithGrantedProviders> GetInternalAsync(
PermissionDefinition permission,
string providerName,
string providerKey)
{
var multiplePermissionWithGrantedProviders = await GetInternalAsync(new PermissionDefinition[] { permission }, providerName, providerKey);
var multiplePermissionWithGrantedProviders = await GetInternalAsync(
new[] { permission },
providerName,
providerKey
);
return multiplePermissionWithGrantedProviders.Result.First();
}
protected virtual async Task<MultiplePermissionWithGrantedProviders> GetInternalAsync(PermissionDefinition[] permissions, string providerName, string providerKey)
protected virtual async Task<MultiplePermissionWithGrantedProviders> GetInternalAsync(
PermissionDefinition[] permissions,
string providerName,
string providerKey)
{
var permissionNames = permissions.Select(x => x.Name).ToArray();
var multiplePermissionWithGrantedProviders = new MultiplePermissionWithGrantedProviders(permissionNames);

5
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionStore.cs

@ -67,7 +67,7 @@ public class PermissionStore : IPermissionStore, ITransientDependency
string currentName,
PermissionGrantCacheItem currentCacheItem)
{
var permissions = PermissionDefinitionManager.GetPermissions();
var permissions = await PermissionDefinitionManager.GetPermissionsAsync();
Logger.LogDebug($"Getting all granted permissions from the repository for this provider name,key: {providerName},{providerKey}");
@ -169,7 +169,8 @@ public class PermissionStore : IPermissionStore, ITransientDependency
string providerKey,
List<string> notCacheKeys)
{
var permissions = PermissionDefinitionManager.GetPermissions().Where(x => notCacheKeys.Any(k => GetPermissionNameFormCacheKeyOrNull(k) == x.Name)).ToList();
var permissions = (await PermissionDefinitionManager.GetPermissionsAsync())
.Where(x => notCacheKeys.Any(k => GetPermissionNameFormCacheKeyOrNull(k) == x.Name)).ToList();
Logger.LogDebug($"Getting not cache granted permissions from the repository for this provider name,key: {providerName},{providerKey}");

294
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionSaver.cs

@ -0,0 +1,294 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
namespace Volo.Abp.PermissionManagement;
public class StaticPermissionSaver : IStaticPermissionSaver, ITransientDependency
{
protected IStaticPermissionDefinitionStore StaticStore { get; }
protected IPermissionGroupDefinitionRecordRepository PermissionGroupRepository { get; }
protected IPermissionDefinitionRecordRepository PermissionRepository { get; }
protected IPermissionDefinitionSerializer PermissionSerializer { get; }
protected IDistributedCache Cache { get; }
protected IApplicationNameAccessor ApplicationNameAccessor { get; }
protected IAbpDistributedLock DistributedLock { get; }
protected PermissionManagementOptions PermissionManagementOptions { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected AbpDistributedCacheOptions CacheOptions { get; }
public StaticPermissionSaver(
IStaticPermissionDefinitionStore staticStore,
IPermissionGroupDefinitionRecordRepository permissionGroupRepository,
IPermissionDefinitionRecordRepository permissionRepository,
IPermissionDefinitionSerializer permissionSerializer,
IDistributedCache cache,
IOptions<AbpDistributedCacheOptions> cacheOptions,
IApplicationNameAccessor applicationNameAccessor,
IAbpDistributedLock distributedLock,
IOptions<PermissionManagementOptions> permissionManagementOptions,
ICancellationTokenProvider cancellationTokenProvider)
{
StaticStore = staticStore;
PermissionGroupRepository = permissionGroupRepository;
PermissionRepository = permissionRepository;
PermissionSerializer = permissionSerializer;
Cache = cache;
ApplicationNameAccessor = applicationNameAccessor;
DistributedLock = distributedLock;
CancellationTokenProvider = cancellationTokenProvider;
PermissionManagementOptions = permissionManagementOptions.Value;
CacheOptions = cacheOptions.Value;
}
[UnitOfWork]
public virtual async Task SaveAsync()
{
await using var applicationLockHandle = await DistributedLock.TryAcquireAsync(
GetApplicationDistributedLockKey()
);
if (applicationLockHandle == null)
{
/* Another application instance is already doing it */
return;
}
/* NOTE: This can be further optimized by using 4 cache values for:
* Groups, permissions, deleted groups and deleted permissions.
* But the code would be more complex. This is enough for now.
*/
var cacheKey = GetApplicationHashCacheKey();
var cachedHash = await Cache.GetStringAsync(cacheKey, CancellationTokenProvider.Token);
var (permissionGroupRecords, permissionRecords) = await PermissionSerializer.SerializeAsync(
await StaticStore.GetGroupsAsync()
);
var currentHash = CalculateHash(
permissionGroupRecords,
permissionRecords,
PermissionManagementOptions.DeletedPermissionGroups,
PermissionManagementOptions.DeletedPermissions
);
if (cachedHash == currentHash)
{
return;
}
await using (var commonLockHandle = await DistributedLock.TryAcquireAsync(
GetCommonDistributedLockKey(),
TimeSpan.FromMinutes(5)))
{
if (commonLockHandle == null)
{
/* It will re-try */
throw new AbpException("Could not acquire distributed lock for saving static permissions!");
}
var hasChangesInGroups = await UpdateChangedPermissionGroupsAsync(permissionGroupRecords);
var hasChangesInPermissions = await UpdateChangedPermissionsAsync(permissionRecords);
if (hasChangesInGroups ||hasChangesInPermissions)
{
await Cache.SetStringAsync(
GetCommonStampCacheKey(),
Guid.NewGuid().ToString(),
new DistributedCacheEntryOptions {
SlidingExpiration = TimeSpan.FromDays(30) //TODO: Make it configurable?
},
CancellationTokenProvider.Token
);
}
}
await Cache.SetStringAsync(
cacheKey,
currentHash,
new DistributedCacheEntryOptions {
SlidingExpiration = TimeSpan.FromDays(30) //TODO: Make it configurable?
},
CancellationTokenProvider.Token
);
}
private async Task<bool> UpdateChangedPermissionGroupsAsync(
IEnumerable<PermissionGroupDefinitionRecord> permissionGroupRecords)
{
var newRecords = new List<PermissionGroupDefinitionRecord>();
var changedRecords = new List<PermissionGroupDefinitionRecord>();
var permissionGroupRecordsInDatabase = (await PermissionGroupRepository.GetListAsync())
.ToDictionary(x => x.Name);
foreach (var permissionGroupRecord in permissionGroupRecords)
{
var permissionGroupRecordInDatabase = permissionGroupRecordsInDatabase.GetOrDefault(permissionGroupRecord.Name);
if (permissionGroupRecordInDatabase == null)
{
/* New group */
newRecords.Add(permissionGroupRecord);
continue;
}
if (permissionGroupRecord.HasSameData(permissionGroupRecordInDatabase))
{
/* Not changed */
continue;
}
/* Changed */
permissionGroupRecordInDatabase.Patch(permissionGroupRecord);
changedRecords.Add(permissionGroupRecordInDatabase);
}
/* Deleted */
var deletedRecords = PermissionManagementOptions.DeletedPermissionGroups.Any()
? permissionGroupRecordsInDatabase.Values
.Where(x => PermissionManagementOptions.DeletedPermissionGroups.Contains(x.Name))
.ToArray()
: Array.Empty<PermissionGroupDefinitionRecord>();
if (newRecords.Any())
{
await PermissionGroupRepository.InsertManyAsync(newRecords);
}
if (changedRecords.Any())
{
await PermissionGroupRepository.UpdateManyAsync(changedRecords);
}
if (deletedRecords.Any())
{
await PermissionGroupRepository.DeleteManyAsync(deletedRecords);
}
return newRecords.Any() || changedRecords.Any() || deletedRecords.Any();
}
private async Task<bool> UpdateChangedPermissionsAsync(
IEnumerable<PermissionDefinitionRecord> permissionRecords)
{
var newRecords = new List<PermissionDefinitionRecord>();
var changedRecords = new List<PermissionDefinitionRecord>();
var permissionRecordsInDatabase = (await PermissionRepository.GetListAsync())
.ToDictionary(x => x.Name);
foreach (var permissionRecord in permissionRecords)
{
var permissionRecordInDatabase = permissionRecordsInDatabase.GetOrDefault(permissionRecord.Name);
if (permissionRecordInDatabase == null)
{
/* New group */
newRecords.Add(permissionRecord);
continue;
}
if (permissionRecord.HasSameData(permissionRecordInDatabase))
{
/* Not changed */
continue;
}
/* Changed */
permissionRecordInDatabase.Patch(permissionRecord);
changedRecords.Add(permissionRecordInDatabase);
}
/* Deleted */
var deletedRecords = new List<PermissionDefinitionRecord>();
if (PermissionManagementOptions.DeletedPermissions.Any())
{
deletedRecords.AddRange(
permissionRecordsInDatabase.Values
.Where(x => PermissionManagementOptions.DeletedPermissions.Contains(x.Name))
);
}
if (PermissionManagementOptions.DeletedPermissionGroups.Any())
{
deletedRecords.AddIfNotContains(
permissionRecordsInDatabase.Values
.Where(x => PermissionManagementOptions.DeletedPermissionGroups.Contains(x.GroupName))
);
}
if (newRecords.Any())
{
await PermissionRepository.InsertManyAsync(newRecords);
}
if (changedRecords.Any())
{
await PermissionRepository.UpdateManyAsync(changedRecords);
}
if (deletedRecords.Any())
{
await PermissionRepository.DeleteManyAsync(deletedRecords);
}
return newRecords.Any() || changedRecords.Any() || deletedRecords.Any();
}
private string GetApplicationDistributedLockKey()
{
return $"{CacheOptions.KeyPrefix}_{ApplicationNameAccessor.ApplicationName}_AbpPermissionUpdateLock";
}
private string GetCommonDistributedLockKey()
{
return $"{CacheOptions.KeyPrefix}_Common_AbpPermissionUpdateLock";
}
private string GetApplicationHashCacheKey()
{
return $"{CacheOptions.KeyPrefix}_{ApplicationNameAccessor.ApplicationName}_AbpPermissionsHash";
}
private string GetCommonStampCacheKey()
{
return $"{CacheOptions.KeyPrefix}_AbpInMemoryPermissionCacheStamp";
}
private static string CalculateHash(
PermissionGroupDefinitionRecord[] permissionGroupRecords,
PermissionDefinitionRecord[] permissionRecords,
IEnumerable<string> deletedPermissionGroups,
IEnumerable<string> deletedPermissions)
{
var stringBuilder = new StringBuilder();
stringBuilder.Append("PermissionGroupRecords:");
stringBuilder.AppendLine(JsonSerializer.Serialize(permissionGroupRecords));
stringBuilder.Append("PermissionRecords:");
stringBuilder.AppendLine(JsonSerializer.Serialize(permissionRecords));
stringBuilder.Append("DeletedPermissionGroups:");
stringBuilder.AppendLine(deletedPermissionGroups.JoinAsString(","));
stringBuilder.Append("DeletedPermission:");
stringBuilder.Append(deletedPermissions.JoinAsString(","));
return stringBuilder
.ToString()
.ToMd5();
}
}

38
modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementDbContextModelBuilderExtensions.cs

@ -1,5 +1,6 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.EntityFrameworkCore.Modeling;
namespace Volo.Abp.PermissionManagement.EntityFrameworkCore;
@ -17,11 +18,44 @@ public static class AbpPermissionManagementDbContextModelBuilderExtensions
b.ConfigureByConvention();
b.Property(x => x.Name).HasMaxLength(PermissionGrantConsts.MaxNameLength).IsRequired();
b.Property(x => x.Name).HasMaxLength(PermissionDefinitionRecordConsts.MaxNameLength).IsRequired();
b.Property(x => x.ProviderName).HasMaxLength(PermissionGrantConsts.MaxProviderNameLength).IsRequired();
b.Property(x => x.ProviderKey).HasMaxLength(PermissionGrantConsts.MaxProviderKeyLength).IsRequired();
b.HasIndex(x => new { x.TenantId, x.Name, x.ProviderName, x.ProviderKey }).IsUnique(true);
b.HasIndex(x => new { x.TenantId, x.Name, x.ProviderName, x.ProviderKey }).IsUnique();
b.ApplyObjectExtensionMappings();
});
builder.Entity<PermissionGroupDefinitionRecord>(b =>
{
b.ToTable(AbpPermissionManagementDbProperties.DbTablePrefix + "PermissionGroups", AbpPermissionManagementDbProperties.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Name).HasMaxLength(PermissionGroupDefinitionRecordConsts.MaxNameLength).IsRequired();
b.Property(x => x.DisplayName).HasMaxLength(PermissionGroupDefinitionRecordConsts.MaxDisplayNameLength).IsRequired();
b.HasIndex(x => new { x.Name }).IsUnique();
b.ApplyObjectExtensionMappings();
});
builder.Entity<PermissionDefinitionRecord>(b =>
{
b.ToTable(AbpPermissionManagementDbProperties.DbTablePrefix + "Permissions", AbpPermissionManagementDbProperties.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.GroupName).HasMaxLength(PermissionGroupDefinitionRecordConsts.MaxNameLength).IsRequired();
b.Property(x => x.Name).HasMaxLength(PermissionDefinitionRecordConsts.MaxNameLength).IsRequired();
b.Property(x => x.ParentName).HasMaxLength(PermissionDefinitionRecordConsts.MaxNameLength);
b.Property(x => x.DisplayName).HasMaxLength(PermissionDefinitionRecordConsts.MaxDisplayNameLength).IsRequired();
b.Property(x => x.Providers).HasMaxLength(PermissionDefinitionRecordConsts.MaxProvidersLength);
b.Property(x => x.StateCheckers).HasMaxLength(PermissionDefinitionRecordConsts.MaxStateCheckersLength);
b.HasIndex(x => new { x.Name }).IsUnique();
b.HasIndex(x => new { x.GroupName });
b.ApplyObjectExtensionMappings();
});

2
modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/AbpPermissionManagementEntityFrameworkCoreModule.cs

@ -14,6 +14,8 @@ public class AbpPermissionManagementEntityFrameworkCoreModule : AbpModule
{
options.AddDefaultRepositories<IPermissionManagementDbContext>();
options.AddRepository<PermissionGroupDefinitionRecord, EfCorePermissionGroupDefinitionRecordRepository>();
options.AddRepository<PermissionDefinitionRecord, EfCorePermissionDefinitionRecordRepository>();
options.AddRepository<PermissionGrant, EfCorePermissionGrantRepository>();
});
}

29
modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/EfCorePermissionDefinitionRecordRepository.cs

@ -0,0 +1,29 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace Volo.Abp.PermissionManagement.EntityFrameworkCore;
public class EfCorePermissionDefinitionRecordRepository :
EfCoreRepository<IPermissionManagementDbContext, PermissionDefinitionRecord, Guid>,
IPermissionDefinitionRecordRepository
{
public EfCorePermissionDefinitionRecordRepository(
IDbContextProvider<IPermissionManagementDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public async Task<PermissionDefinitionRecord> FindByNameAsync(
string name,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
.OrderBy(x => x.Id)
.FirstOrDefaultAsync(r => r.Name == name, cancellationToken);
}
}

3
modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/EfCorePermissionGrantRepository.cs

@ -9,7 +9,8 @@ using Volo.Abp.EntityFrameworkCore;
namespace Volo.Abp.PermissionManagement.EntityFrameworkCore;
public class EfCorePermissionGrantRepository : EfCoreRepository<IPermissionManagementDbContext, PermissionGrant, Guid>,
public class EfCorePermissionGrantRepository :
EfCoreRepository<IPermissionManagementDbContext, PermissionGrant, Guid>,
IPermissionGrantRepository
{
public EfCorePermissionGrantRepository(IDbContextProvider<IPermissionManagementDbContext> dbContextProvider)

16
modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/EfCorePermissionGroupDefinitionRecordRepository.cs

@ -0,0 +1,16 @@
using System;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace Volo.Abp.PermissionManagement.EntityFrameworkCore;
public class EfCorePermissionGroupDefinitionRecordRepository :
EfCoreRepository<IPermissionManagementDbContext, PermissionGroupDefinitionRecord, Guid>,
IPermissionGroupDefinitionRecordRepository
{
public EfCorePermissionGroupDefinitionRecordRepository(
IDbContextProvider<IPermissionManagementDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}

4
modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/IPermissionManagementDbContext.cs

@ -7,5 +7,9 @@ namespace Volo.Abp.PermissionManagement.EntityFrameworkCore;
[ConnectionStringName(AbpPermissionManagementDbProperties.ConnectionStringName)]
public interface IPermissionManagementDbContext : IEfCoreDbContext
{
DbSet<PermissionGroupDefinitionRecord> PermissionGroups { get; }
DbSet<PermissionDefinitionRecord> Permissions { get; }
DbSet<PermissionGrant> PermissionGrants { get; }
}

2
modules/permission-management/src/Volo.Abp.PermissionManagement.EntityFrameworkCore/Volo/Abp/PermissionManagement/EntityFrameworkCore/PermissionManagementDbContext.cs

@ -7,6 +7,8 @@ namespace Volo.Abp.PermissionManagement.EntityFrameworkCore;
[ConnectionStringName(AbpPermissionManagementDbProperties.ConnectionStringName)]
public class PermissionManagementDbContext : AbpDbContext<PermissionManagementDbContext>, IPermissionManagementDbContext
{
public DbSet<PermissionGroupDefinitionRecord> PermissionGroups { get; set; }
public DbSet<PermissionDefinitionRecord> Permissions { get; set; }
public DbSet<PermissionGrant> PermissionGrants { get; set; }
public PermissionManagementDbContext(DbContextOptions<PermissionManagementDbContext> options)

10
modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbContextExtensions.cs

@ -9,6 +9,16 @@ public static class AbpPermissionManagementMongoDbContextExtensions
{
Check.NotNull(builder, nameof(builder));
builder.Entity<PermissionGroupDefinitionRecord>(b =>
{
b.CollectionName = AbpPermissionManagementDbProperties.DbTablePrefix + "PermissionGroups";
});
builder.Entity<PermissionDefinitionRecord>(b =>
{
b.CollectionName = AbpPermissionManagementDbProperties.DbTablePrefix + "Permissions";
});
builder.Entity<PermissionGrant>(b =>
{
b.CollectionName = AbpPermissionManagementDbProperties.DbTablePrefix + "PermissionGrants";

2
modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbModule.cs

@ -16,6 +16,8 @@ public class AbpPermissionManagementMongoDbModule : AbpModule
{
options.AddDefaultRepositories<IPermissionManagementMongoDbContext>();
options.AddRepository<PermissionGroupDefinitionRecord, MongoPermissionGroupDefinitionRecordRepository>();
options.AddRepository<PermissionDefinitionRecord, MongoPermissionDefinitionRecordRepository>();
options.AddRepository<PermissionGrant, MongoPermissionGrantRepository>();
});
}

4
modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/IPermissionManagementMongoDbContext.cs

@ -7,5 +7,9 @@ namespace Volo.Abp.PermissionManagement.MongoDB;
[ConnectionStringName(AbpPermissionManagementDbProperties.ConnectionStringName)]
public interface IPermissionManagementMongoDbContext : IAbpMongoDbContext
{
IMongoCollection<PermissionGroupDefinitionRecord> PermissionGroups { get; }
IMongoCollection<PermissionDefinitionRecord> Permissions { get; }
IMongoCollection<PermissionGrant> PermissionGrants { get; }
}

32
modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/MongoPermissionDefinitionRecordRepository.cs

@ -0,0 +1,32 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Driver.Linq;
using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.MongoDB;
namespace Volo.Abp.PermissionManagement.MongoDB;
public class MongoPermissionDefinitionRecordRepository :
MongoDbRepository<IPermissionManagementMongoDbContext, PermissionDefinitionRecord, Guid>,
IPermissionDefinitionRecordRepository
{
public MongoPermissionDefinitionRecordRepository(
IMongoDbContextProvider<IPermissionManagementMongoDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public async Task<PermissionDefinitionRecord> FindByNameAsync(
string name,
CancellationToken cancellationToken = default)
{
cancellationToken = GetCancellationToken(cancellationToken);
return await (await GetMongoQueryableAsync(cancellationToken))
.OrderBy(x => x.Id)
.FirstOrDefaultAsync(
s => s.Name == name,
cancellationToken
);
}
}

4
modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/MongoPermissionGrantRepository.cs

@ -10,7 +10,9 @@ using Volo.Abp.MongoDB;
namespace Volo.Abp.PermissionManagement.MongoDB;
public class MongoPermissionGrantRepository : MongoDbRepository<IPermissionManagementMongoDbContext, PermissionGrant, Guid>, IPermissionGrantRepository
public class MongoPermissionGrantRepository :
MongoDbRepository<IPermissionManagementMongoDbContext, PermissionGrant, Guid>,
IPermissionGrantRepository
{
public MongoPermissionGrantRepository(IMongoDbContextProvider<IPermissionManagementMongoDbContext> dbContextProvider)
: base(dbContextProvider)

16
modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/MongoPermissionGroupDefinitionRecordRepository.cs

@ -0,0 +1,16 @@
using System;
using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.MongoDB;
namespace Volo.Abp.PermissionManagement.MongoDB;
public class MongoPermissionGroupDefinitionRecordRepository :
MongoDbRepository<IPermissionManagementMongoDbContext, PermissionGroupDefinitionRecord, Guid>,
IPermissionGroupDefinitionRecordRepository
{
public MongoPermissionGroupDefinitionRecordRepository(
IMongoDbContextProvider<IPermissionManagementMongoDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}

2
modules/permission-management/src/Volo.Abp.PermissionManagement.MongoDB/Volo/Abp/PermissionManagement/MongoDb/PermissionManagementMongoDbContext.cs

@ -7,6 +7,8 @@ namespace Volo.Abp.PermissionManagement.MongoDB;
[ConnectionStringName(AbpPermissionManagementDbProperties.ConnectionStringName)]
public class PermissionManagementMongoDbContext : AbpMongoDbContext, IPermissionManagementMongoDbContext
{
public IMongoCollection<PermissionGroupDefinitionRecord> PermissionGroups => Collection<PermissionGroupDefinitionRecord>();
public IMongoCollection<PermissionDefinitionRecord> Permissions => Collection<PermissionDefinitionRecord>();
public IMongoCollection<PermissionGrant> PermissionGrants => Collection<PermissionGrant>();
protected override void CreateModel(IMongoModelBuilder modelBuilder)

55
modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml.cs

@ -1,11 +1,14 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Localization;
using Volo.Abp.PermissionManagement.Web.Utils;
namespace Volo.Abp.PermissionManagement.Web.Pages.AbpPermissionManagement;
@ -37,15 +40,18 @@ public class PermissionManagementModal : AbpPageModel
protected IPermissionAppService PermissionAppService { get; }
protected ILocalEventBus LocalEventBus { get; }
public AbpLocalizationOptions LocalizationOptions { get; }
public PermissionManagementModal(
IPermissionAppService permissionAppService,
ILocalEventBus localEventBus)
IPermissionAppService permissionAppService,
ILocalEventBus localEventBus,
IOptions<AbpLocalizationOptions> localizationOptions)
{
ObjectMapperContext = typeof(AbpPermissionManagementWebModule);
PermissionAppService = permissionAppService;
LocalEventBus = localEventBus;
LocalizationOptions = localizationOptions.Value;
}
public virtual async Task<IActionResult> OnGetAsync()
@ -54,6 +60,8 @@ public class PermissionManagementModal : AbpPageModel
var result = await PermissionAppService.GetAsync(ProviderName, ProviderKey);
UpdateLocalizations(result);
EntityDisplayName = !string.IsNullOrWhiteSpace(ProviderKeyDisplayName)
? ProviderKeyDisplayName
: result.EntityDisplayName;
@ -78,6 +86,49 @@ public class PermissionManagementModal : AbpPageModel
return Page();
}
private void UpdateLocalizations(GetPermissionListResultDto result)
{
foreach (var group in result.Groups)
{
group.DisplayName = Localize(
group.DisplayNameKey,
group.DisplayNameResource,
group.DisplayName
);
foreach (var permission in group.Permissions)
{
permission.DisplayName = Localize(
permission.DisplayNameKey,
permission.DisplayNameResource,
permission.DisplayName
);
}
}
}
private string Localize(string key, string resourceName, string fallbackValue)
{
if (key.IsNullOrEmpty() || resourceName.IsNullOrEmpty())
{
return fallbackValue;
}
var resource = LocalizationOptions.Resources.GetOrNull(resourceName);
if (resource == null)
{
return fallbackValue;
}
var result = new LocalizableString(resource.ResourceType, key).Localize(StringLocalizerFactory);
if (result.ResourceNotFound)
{
return fallbackValue;
}
return result.Value;
}
public virtual async Task<IActionResult> OnPostAsync()
{
ValidateModel();

2
modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj

@ -12,6 +12,8 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Features\Volo.Abp.Features.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.GlobalFeatures\Volo.Abp.GlobalFeatures.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.PermissionManagement.EntityFrameworkCore\Volo.Abp.PermissionManagement.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\Volo.Abp.PermissionManagement.TestBase\Volo.Abp.PermissionManagement.TestBase.csproj" />
</ItemGroup>

7
modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/AbpPermissionManagementTestModule.cs

@ -2,6 +2,8 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Features;
using Volo.Abp.GlobalFeatures;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.Uow;
@ -10,7 +12,10 @@ namespace Volo.Abp.PermissionManagement;
[DependsOn(
typeof(AbpPermissionManagementEntityFrameworkCoreModule),
typeof(AbpPermissionManagementTestBaseModule))]
typeof(AbpPermissionManagementTestBaseModule),
typeof(AbpFeaturesModule),
typeof(AbpGlobalFeaturesModule)
)]
public class AbpPermissionManagementTestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save