diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleBatchStateChecker.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleBatchStateChecker.cs index ad6c9b989e..0957f51666 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleBatchStateChecker.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleBatchStateChecker.cs @@ -11,15 +11,21 @@ namespace Volo.Abp.Authorization.Permissions; public class RequirePermissionsSimpleBatchStateChecker : SimpleBatchStateCheckerBase where TState : IHasSimpleStateCheckers { - public static RequirePermissionsSimpleBatchStateChecker Current => _current.Value!; - private static readonly AsyncLocal> _current = new AsyncLocal>(); - - private readonly List> _models; - - static RequirePermissionsSimpleBatchStateChecker() + public static RequirePermissionsSimpleBatchStateChecker Current { - _current.Value = new RequirePermissionsSimpleBatchStateChecker(); + get + { + if (_current.Value == null) + { + _current.Value = new RequirePermissionsSimpleBatchStateChecker(); + } + + return _current.Value; + } } + private static readonly AsyncLocal?> _current = new AsyncLocal?>(); + + private readonly List> _models; public RequirePermissionsSimpleBatchStateChecker() { diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/RequireFeaturesSimpleBatchStateChecker.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/RequireFeaturesSimpleBatchStateChecker.cs index 24089e2364..66d484f7f9 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/RequireFeaturesSimpleBatchStateChecker.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/RequireFeaturesSimpleBatchStateChecker.cs @@ -11,15 +11,21 @@ namespace Volo.Abp.Features; public class RequireFeaturesSimpleBatchStateChecker : SimpleBatchStateCheckerBase where TState : IHasSimpleStateCheckers { - public static RequireFeaturesSimpleBatchStateChecker Current => _current.Value!; - private static readonly AsyncLocal> _current = new(); - - private readonly List> _models; - - static RequireFeaturesSimpleBatchStateChecker() + public static RequireFeaturesSimpleBatchStateChecker Current { - _current.Value = new RequireFeaturesSimpleBatchStateChecker(); + get + { + if (_current.Value == null) + { + _current.Value = new RequireFeaturesSimpleBatchStateChecker(); + } + + return _current.Value; + } } + private static readonly AsyncLocal?> _current = new(); + + private readonly List> _models; public RequireFeaturesSimpleBatchStateChecker() { diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo.Abp.Authorization.Tests.csproj b/framework/test/Volo.Abp.Authorization.Tests/Volo.Abp.Authorization.Tests.csproj index 9a7119a6a9..f1deb2761a 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo.Abp.Authorization.Tests.csproj +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo.Abp.Authorization.Tests.csproj @@ -13,6 +13,7 @@ + diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs index 15ec4e0641..70a3bf1aa0 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs @@ -5,6 +5,7 @@ using Volo.Abp.Authorization.TestServices.Resources; using Volo.Abp.Autofac; using Volo.Abp.DynamicProxy; using Volo.Abp.ExceptionHandling; +using Volo.Abp.Features; using Volo.Abp.Modularity; namespace Volo.Abp.Authorization; @@ -12,6 +13,7 @@ namespace Volo.Abp.Authorization; [DependsOn(typeof(AbpAutofacModule))] [DependsOn(typeof(AbpAuthorizationModule))] [DependsOn(typeof(AbpExceptionHandlingModule))] +[DependsOn(typeof(AbpFeaturesModule))] public class AbpAuthorizationTestModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs index a32939fa53..33058e5468 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs @@ -69,7 +69,8 @@ public class Authorization_Tests : AuthorizationTestBase [Fact] public async Task Should_Permission_Definition_GetGroup() { - (await _permissionDefinitionManager.GetGroupsAsync()).Count.ShouldBe(1); + var groups = await _permissionDefinitionManager.GetGroupsAsync(); + groups.ShouldContain(g => g.Name == "TestGroup"); } [Fact] diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/RequirePermissionsSimpleBatchStateChecker_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/RequirePermissionsSimpleBatchStateChecker_Tests.cs index d52718fd50..6bcc214623 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/RequirePermissionsSimpleBatchStateChecker_Tests.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/RequirePermissionsSimpleBatchStateChecker_Tests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Shouldly; using Volo.Abp.Authorization.Permissions; @@ -56,6 +57,21 @@ public class RequirePermissionsSimpleBatchStateChecker_Tests : AuthorizationTest result[myStateEntities[3]].ShouldBeTrue(); } + [Fact] + public async Task Current_Should_Not_Be_Null_In_Fresh_ExecutionContext() + { + _ = RequirePermissionsSimpleBatchStateChecker.Current; + + Task> task; + using (ExecutionContext.SuppressFlow()) + { + task = Task.Run(() => RequirePermissionsSimpleBatchStateChecker.Current); + } + + var current = await task; + current.ShouldNotBeNull(); + } + class MyStateEntity : IHasSimpleStateCheckers { public List> StateCheckers { get; } @@ -75,4 +91,14 @@ public class RequirePermissionsSimpleBatchStateChecker_Tests : AuthorizationTest StateCheckers = new List>(); } } + + class MyStateEntity3 : IHasSimpleStateCheckers + { + public List> StateCheckers { get; } + + public MyStateEntity3() + { + StateCheckers = new List>(); + } + } } diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/StaticPermissionDefinitionStore_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/StaticPermissionDefinitionStore_Tests.cs index 19ab829ba1..a2d7f43b13 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/StaticPermissionDefinitionStore_Tests.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/StaticPermissionDefinitionStore_Tests.cs @@ -1,7 +1,10 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using Shouldly; using Volo.Abp.Authorization.Permissions; using Volo.Abp.Authorization.TestServices.Resources; +using Volo.Abp.StaticDefinitions; using Xunit; namespace Volo.Abp.Authorization; @@ -84,4 +87,37 @@ public class StaticPermissionDefinitionStore_Tests : AuthorizationTestBase permissions.ShouldContain(x => x.Name == "MyResourcePermission7" && x.ResourceName == TestEntityResource2.ResourceName); } + + [Fact] + public async Task Should_Rebuild_Definitions_In_Fresh_ExecutionContext_After_Cache_Clear() + { + var groupCache = GetRequiredService, List)>>(); + var definitionCache = GetRequiredService>>(); + + await groupCache.ClearAsync(); + await definitionCache.ClearAsync(); + + // Touch the type initializer (if any) on the test ExecutionContext first, mirroring + // the production scenario where startup pre-warms it on a different ExecutionContext. + _ = await _store.GetOrNullAsync("FeatureGatedPermission"); + + await groupCache.ClearAsync(); + await definitionCache.ClearAsync(); + + PermissionDefinition permission = null; + Task task; + using (ExecutionContext.SuppressFlow()) + { + task = Task.Run(async () => + { + permission = await _store.GetOrNullAsync("FeatureGatedPermission"); + }); + } + await task; + + permission.ShouldNotBeNull(); + permission.Name.ShouldBe("FeatureGatedPermission"); + } } diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/FeatureGatedTestPermissionDefinitionProvider.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/FeatureGatedTestPermissionDefinitionProvider.cs new file mode 100644 index 0000000000..0fcfe7c259 --- /dev/null +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/FeatureGatedTestPermissionDefinitionProvider.cs @@ -0,0 +1,15 @@ +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Features; + +namespace Volo.Abp.Authorization.TestServices; + +public class FeatureGatedTestPermissionDefinitionProvider : PermissionDefinitionProvider +{ + public override void Define(IPermissionDefinitionContext context) + { + var group = context.AddGroup("FeatureGatedTestGroup"); + + group.AddPermission("FeatureGatedPermission") + .RequireFeatures("FeatureGatedTestFeature"); + } +} diff --git a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/RequireFeaturesSimpleBatchStateChecker_Tests.cs b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/RequireFeaturesSimpleBatchStateChecker_Tests.cs index 2ea3f2916b..b0775e70d2 100644 --- a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/RequireFeaturesSimpleBatchStateChecker_Tests.cs +++ b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/RequireFeaturesSimpleBatchStateChecker_Tests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Shouldly; using Volo.Abp.MultiTenancy; @@ -84,6 +85,21 @@ public class RequireFeaturesSimpleBatchStateChecker_Tests : FeatureTestBase } } + [Fact] + public async Task Current_Should_Not_Be_Null_In_Fresh_ExecutionContext() + { + _ = RequireFeaturesSimpleBatchStateChecker.Current; + + Task> task; + using (ExecutionContext.SuppressFlow()) + { + task = Task.Run(() => RequireFeaturesSimpleBatchStateChecker.Current); + } + + var current = await task; + current.ShouldNotBeNull(); + } + class MyStateEntity : IHasSimpleStateCheckers { public List> StateCheckers { get; } @@ -103,4 +119,14 @@ public class RequireFeaturesSimpleBatchStateChecker_Tests : FeatureTestBase StateCheckers = new List>(); } } + + class MyStateEntity3 : IHasSimpleStateCheckers + { + public List> StateCheckers { get; } + + public MyStateEntity3() + { + StateCheckers = new List>(); + } + } }