From a2d39ad8b3070b89ba6f0bcab1673907731c71dc Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 13 Nov 2025 10:00:57 +0800 Subject: [PATCH] Implement resource-based permission management with new methods and classes --- .../IPermissionDefinitionContext.cs | 13 ++++ .../IPermissionDefinitionManager.cs | 7 ++ .../Permissions/PermissionDefinition.cs | 14 +++- .../PermissionDefinitionContext.cs | 73 ++++++++++++++----- .../Permissions/PermissionGroupDefinition.cs | 31 -------- .../ResourcePermissionRequirement.cs | 21 ++++++ .../IDynamicPermissionDefinitionStore.cs | 8 +- .../IStaticPermissionDefinitionStore.cs | 8 +- .../NullDynamicPermissionDefinitionStore.cs | 19 ++++- .../PermissionDefinitionManager.cs | 44 +++++++++-- .../StaticPermissionDefinitionStore.cs | 32 +++++--- ...sourcePermissionAuthorizationExtensions.cs | 13 ++++ ...ityResourcePermissionRequirementHandler.cs | 30 ++++++++ 13 files changed, 240 insertions(+), 73 deletions(-) create mode 100644 framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/ResourcePermissionRequirement.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/EntityResourcePermissionAuthorizationExtensions.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Authorization/Permissions/Resources/EntityResourcePermissionRequirementHandler.cs diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs index 7ff890bac7..e6c949429d 100644 --- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionContext.cs @@ -1,5 +1,7 @@ using System; +using JetBrains.Annotations; using Volo.Abp.Localization; +using Volo.Abp.MultiTenancy; namespace Volo.Abp.Authorization.Permissions; @@ -46,4 +48,15 @@ public interface IPermissionDefinitionContext /// Name of the permission /// PermissionDefinition? GetPermissionOrNull(string name); + + PermissionDefinition AddResourcePermission( + string name, + string resourceName, + ILocalizableString? displayName = null, + MultiTenancySides multiTenancySide = MultiTenancySides.Both, + bool isEnabled = true); + + PermissionDefinition? GetResourcePermissionOrNull([NotNull] string name); + + void RemoveResourcePermission([NotNull] string name); } diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs index ca04b110f7..52afcbe6eb 100644 --- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionDefinitionManager.cs @@ -11,7 +11,14 @@ public interface IPermissionDefinitionManager Task GetOrNullAsync([NotNull] string name); + [ItemNotNull] + Task GetResourcePermissionAsync([NotNull] string name); + + Task GetResourcePermissionOrNullAsync([NotNull] string name); + Task> GetPermissionsAsync(); + Task> GetResourcePermissionsAsync(); + Task> GetGroupsAsync(); } diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs index 9f527487e0..5e4cdac85a 100644 --- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs @@ -83,13 +83,22 @@ public class PermissionDefinition : protected internal PermissionDefinition( [NotNull] string name, - string? resourceName = null, + string resourceName, + ILocalizableString? displayName = null, + MultiTenancySides multiTenancySide = MultiTenancySides.Both, + bool isEnabled = true) + : this(name, displayName, multiTenancySide, isEnabled) + { + ResourceName = Check.NotNull(resourceName, nameof(resourceName)); + } + + protected internal PermissionDefinition( + [NotNull] string name, ILocalizableString? displayName = null, MultiTenancySides multiTenancySide = MultiTenancySides.Both, bool isEnabled = true) { Name = Check.NotNull(name, nameof(name)); - ResourceName = resourceName; DisplayName = displayName ?? new FixedLocalizableString(name); MultiTenancySide = multiTenancySide; IsEnabled = isEnabled; @@ -108,7 +117,6 @@ public class PermissionDefinition : { var child = new PermissionDefinition( name, - null, displayName, multiTenancySide, isEnabled) diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs index 394cdb9d82..83ffda1f80 100644 --- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using JetBrains.Annotations; using Volo.Abp.Localization; +using Volo.Abp.MultiTenancy; namespace Volo.Abp.Authorization.Permissions; @@ -11,17 +12,20 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext public Dictionary Groups { get; } + public Dictionary ResourcePermissions { get; } + internal IPermissionDefinitionProvider? CurrentProvider { get; set; } public static class KnownPropertyNames { public const string CurrentProviderName = "_CurrentProviderName"; } - + public PermissionDefinitionContext(IServiceProvider serviceProvider) { ServiceProvider = serviceProvider; Groups = new Dictionary(); + ResourcePermissions = new Dictionary(); } public virtual PermissionGroupDefinition AddGroup( @@ -43,7 +47,7 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext } Groups[name] = group; - + return group; } @@ -51,37 +55,23 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext public virtual PermissionGroupDefinition GetGroup([NotNull] string name) { var group = GetGroupOrNull(name); - - if (group == null) - { - throw new AbpException($"Could not find a permission definition group with the given name: {name}"); - } - - return group; + return group ?? throw new AbpException($"Could not find a permission definition group with the given name: {name}"); } public virtual PermissionGroupDefinition? GetGroupOrNull([NotNull] string name) { Check.NotNull(name, nameof(name)); - - if (!Groups.ContainsKey(name)) - { - return null; - } - - return Groups[name]; + return Groups.GetOrDefault(name); } public virtual void RemoveGroup(string name) { Check.NotNull(name, nameof(name)); - if (!Groups.ContainsKey(name)) + if (!Groups.Remove(name)) { throw new AbpException($"Not found permission group with name: {name}"); } - - Groups.Remove(name); } public virtual PermissionDefinition? GetPermissionOrNull([NotNull] string name) @@ -100,4 +90,49 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext return null; } + + public virtual PermissionDefinition AddResourcePermission( + string name, + string resourceName, + ILocalizableString? displayName = null, + MultiTenancySides multiTenancySide = MultiTenancySides.Both, + bool isEnabled = true) + { + Check.NotNull(name, nameof(name)); + + if (ResourcePermissions.ContainsKey(name)) + { + throw new AbpException($"There is already an existing resource permission with name: {name}"); + } + + var permission = new PermissionDefinition( + name, + resourceName, + displayName, + multiTenancySide, + isEnabled) + { + [KnownPropertyNames.CurrentProviderName] = CurrentProvider?.GetType().FullName + }; + + ResourcePermissions[name] = permission; + + return permission; + } + + public virtual PermissionDefinition? GetResourcePermissionOrNull([NotNull] string name) + { + Check.NotNull(name, nameof(name)); + return ResourcePermissions.GetOrDefault(name); + } + + public virtual void RemoveResourcePermission([NotNull] string name) + { + Check.NotNull(name, nameof(name)); + + if (!ResourcePermissions.Remove(name)) + { + throw new AbpException($"Not found resource permission with name: {name}"); + } + } } diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionGroupDefinition.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionGroupDefinition.cs index a064245488..0ad2dca099 100644 --- a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionGroupDefinition.cs +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionGroupDefinition.cs @@ -53,40 +53,9 @@ public class PermissionGroupDefinition : ICanAddChildPermission ILocalizableString? displayName = null, MultiTenancySides multiTenancySide = MultiTenancySides.Both, bool isEnabled = true) - { - return AddPermission( - name, - null, - displayName, - multiTenancySide, - isEnabled); - } - - public virtual PermissionDefinition AddResourcePermission( - [NotNull] string name, - [NotNull] string resourceName, - ILocalizableString? displayName = null, - MultiTenancySides multiTenancySide = MultiTenancySides.Both, - bool isEnabled = true) - { - return AddPermission( - name, - resourceName, - displayName, - multiTenancySide, - isEnabled); - } - - protected virtual PermissionDefinition AddPermission( - [NotNull] string name, - string? resourceName, - ILocalizableString? displayName = null, - MultiTenancySides multiTenancySide = MultiTenancySides.Both, - bool isEnabled = true) { var permission = new PermissionDefinition( name, - resourceName, displayName, multiTenancySide, isEnabled diff --git a/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/ResourcePermissionRequirement.cs b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/ResourcePermissionRequirement.cs new file mode 100644 index 0000000000..03658f3e90 --- /dev/null +++ b/framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/ResourcePermissionRequirement.cs @@ -0,0 +1,21 @@ +using JetBrains.Annotations; +using Microsoft.AspNetCore.Authorization; + +namespace Volo.Abp.Authorization; + +public class ResourcePermissionRequirement : IAuthorizationRequirement +{ + public string PermissionName { get; } + + public ResourcePermissionRequirement([NotNull] string permissionName) + { + Check.NotNull(permissionName, nameof(permissionName)); + + PermissionName = permissionName; + } + + public override string ToString() + { + return $"ResourcePermissionRequirement: {PermissionName}"; + } +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IDynamicPermissionDefinitionStore.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IDynamicPermissionDefinitionStore.cs index 366ae2e58a..49f798675a 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IDynamicPermissionDefinitionStore.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IDynamicPermissionDefinitionStore.cs @@ -8,6 +8,10 @@ public interface IDynamicPermissionDefinitionStore Task GetOrNullAsync(string name); Task> GetPermissionsAsync(); - + + Task GetResourcePermissionOrNullAsync(string name); + + Task> GetResourcePermissionsAsync(); + Task> GetGroupsAsync(); -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IStaticPermissionDefinitionStore.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IStaticPermissionDefinitionStore.cs index 4da8423dd3..74899574e7 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IStaticPermissionDefinitionStore.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IStaticPermissionDefinitionStore.cs @@ -8,6 +8,10 @@ public interface IStaticPermissionDefinitionStore Task GetOrNullAsync(string name); Task> GetPermissionsAsync(); - + + Task GetResourcePermissionOrNullAsync(string name); + + Task> GetResourcePermissionsAsync(); + Task> GetGroupsAsync(); -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/NullDynamicPermissionDefinitionStore.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/NullDynamicPermissionDefinitionStore.cs index 5b13288153..4ea7990abd 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/NullDynamicPermissionDefinitionStore.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/NullDynamicPermissionDefinitionStore.cs @@ -9,10 +9,15 @@ namespace Volo.Abp.Authorization.Permissions; public class NullDynamicPermissionDefinitionStore : IDynamicPermissionDefinitionStore, ISingletonDependency { private readonly static Task CachedPermissionResult = Task.FromResult((PermissionDefinition?)null); - + private readonly static Task> CachedPermissionsResult = Task.FromResult((IReadOnlyList)Array.Empty().ToImmutableList()); + private readonly static Task CachedResourcePermissionResult = Task.FromResult((PermissionDefinition?)null); + + private readonly static Task> CachedResourcePermissionsResult = + Task.FromResult((IReadOnlyList)Array.Empty().ToImmutableList()); + private readonly static Task> CachedGroupsResult = Task.FromResult((IReadOnlyList)Array.Empty().ToImmutableList()); @@ -26,8 +31,18 @@ public class NullDynamicPermissionDefinitionStore : IDynamicPermissionDefinition return CachedPermissionsResult; } + public Task GetResourcePermissionOrNullAsync(string name) + { + return CachedResourcePermissionResult; + } + + public Task> GetResourcePermissionsAsync() + { + return CachedResourcePermissionsResult; + } + public Task> GetGroupsAsync() { return CachedGroupsResult; } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs index 23f9e7883e..41c261de49 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs @@ -34,17 +34,36 @@ public class PermissionDefinitionManager : IPermissionDefinitionManager, ITransi { Check.NotNull(name, nameof(name)); - return await _staticStore.GetOrNullAsync(name) ?? + return await _staticStore.GetOrNullAsync(name) ?? await _dynamicStore.GetOrNullAsync(name); } + public virtual async Task GetResourcePermissionAsync(string name) + { + var permission = await GetResourcePermissionOrNullAsync(name); + if (permission == null) + { + throw new AbpException("Undefined permission: " + name); + } + + return permission; + } + + public virtual async Task GetResourcePermissionOrNullAsync(string name) + { + Check.NotNull(name, nameof(name)); + + return await _staticStore.GetResourcePermissionOrNullAsync(name) ?? + await _dynamicStore.GetResourcePermissionOrNullAsync(name); + } + public virtual async Task> GetPermissionsAsync() { var staticPermissions = await _staticStore.GetPermissionsAsync(); var staticPermissionNames = staticPermissions .Select(p => p.Name) .ToImmutableHashSet(); - + var dynamicPermissions = await _dynamicStore.GetPermissionsAsync(); /* We prefer static permissions over dynamics */ @@ -53,13 +72,28 @@ public class PermissionDefinitionManager : IPermissionDefinitionManager, ITransi ).ToImmutableList(); } - public async Task> GetGroupsAsync() + public virtual async Task> GetResourcePermissionsAsync() + { + var staticResourcePermissions = await _staticStore.GetResourcePermissionsAsync(); + var staticResourcePermissionNames = staticResourcePermissions + .Select(p => p.Name) + .ToImmutableHashSet(); + + var dynamicResourcePermissions = await _dynamicStore.GetResourcePermissionsAsync(); + + /* We prefer static permissions over dynamics */ + return staticResourcePermissions.Concat( + dynamicResourcePermissions.Where(d => !staticResourcePermissionNames.Contains(d.Name)) + ).ToImmutableList(); + } + + public virtual async Task> GetGroupsAsync() { var staticGroups = await _staticStore.GetGroupsAsync(); var staticGroupNames = staticGroups .Select(p => p.Name) .ToImmutableHashSet(); - + var dynamicGroups = await _dynamicStore.GetGroupsAsync(); /* We prefer static groups over dynamics */ @@ -67,4 +101,4 @@ public class PermissionDefinitionManager : IPermissionDefinitionManager, ITransi dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name)) ).ToImmutableList(); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs index 4e6ff0d11c..938aaf3440 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs @@ -11,12 +11,14 @@ namespace Volo.Abp.Authorization.Permissions; public class StaticPermissionDefinitionStore : IStaticPermissionDefinitionStore, ISingletonDependency { - protected IDictionary PermissionGroupDefinitions => _lazyPermissionGroupDefinitions.Value; - private readonly Lazy> _lazyPermissionGroupDefinitions; + protected IDictionary PermissionGroupDefinitions => _lazyPermissionGroupDefinitions.Value.Item1; + private readonly Lazy<(Dictionary, Dictionary)> _lazyPermissionGroupDefinitions; protected IDictionary PermissionDefinitions => _lazyPermissionDefinitions.Value; private readonly Lazy> _lazyPermissionDefinitions; + protected IDictionary ResourcePermissionDefinitions => _lazyPermissionGroupDefinitions.Value.Item2; + protected AbpPermissionOptions Options { get; } private readonly IServiceProvider _serviceProvider; @@ -33,12 +35,12 @@ public class StaticPermissionDefinitionStore : IStaticPermissionDefinitionStore, isThreadSafe: true ); - _lazyPermissionGroupDefinitions = new Lazy>( + _lazyPermissionGroupDefinitions = new Lazy<(Dictionary, Dictionary)>( CreatePermissionGroupDefinitions, isThreadSafe: true ); } - + protected virtual Dictionary CreatePermissionDefinitions() { var permissions = new Dictionary(); @@ -71,7 +73,7 @@ public class StaticPermissionDefinitionStore : IStaticPermissionDefinitionStore, } } - protected virtual Dictionary CreatePermissionGroupDefinitions() + protected virtual (Dictionary, Dictionary) CreatePermissionGroupDefinitions() { using (var scope = _serviceProvider.CreateScope()) { @@ -99,10 +101,10 @@ public class StaticPermissionDefinitionStore : IStaticPermissionDefinitionStore, context.CurrentProvider = provider; provider.PostDefine(context); } - + context.CurrentProvider = null; - return context.Groups; + return (context.Groups, context.ResourcePermissions); } } @@ -110,7 +112,7 @@ public class StaticPermissionDefinitionStore : IStaticPermissionDefinitionStore, { return Task.FromResult(PermissionDefinitions.GetOrDefault(name)); } - + public virtual Task> GetPermissionsAsync() { return Task.FromResult>( @@ -118,10 +120,22 @@ public class StaticPermissionDefinitionStore : IStaticPermissionDefinitionStore, ); } + public virtual Task GetResourcePermissionOrNullAsync(string name) + { + return Task.FromResult(ResourcePermissionDefinitions.GetOrDefault(name)); + } + + public virtual Task> GetResourcePermissionsAsync() + { + return Task.FromResult>( + ResourcePermissionDefinitions.Values.ToImmutableList() + ); + } + public Task> GetGroupsAsync() { return Task.FromResult>( PermissionGroupDefinitions.Values.ToImmutableList() ); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/EntityResourcePermissionAuthorizationExtensions.cs b/framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/EntityResourcePermissionAuthorizationExtensions.cs new file mode 100644 index 0000000000..e392d6378d --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/EntityResourcePermissionAuthorizationExtensions.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Authorization; +using Volo.Abp.Authorization.Permissions.Resources; + +namespace Microsoft.Extensions.DependencyInjection; + +public static class ResourcePermissionExtenstions +{ + public static IServiceCollection AddEntityResourcePermissionAuthorization(this IServiceCollection services) + { + services.AddSingleton(); + return services; + } +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Authorization/Permissions/Resources/EntityResourcePermissionRequirementHandler.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Authorization/Permissions/Resources/EntityResourcePermissionRequirementHandler.cs new file mode 100644 index 0000000000..aff721d90e --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Authorization/Permissions/Resources/EntityResourcePermissionRequirementHandler.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Volo.Abp.Domain.Entities; + +namespace Volo.Abp.Authorization.Permissions.Resources; + +public class EntityResourcePermissionRequirementHandler : AuthorizationHandler +{ + protected readonly IResourcePermissionChecker PermissionChecker; + + protected EntityResourcePermissionRequirementHandler(IResourcePermissionChecker permissionChecker) + { + PermissionChecker = permissionChecker; + } + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ResourcePermissionRequirement requirement, IEntity? resource) + { + if (resource == null) + { + return; + } + + var resourceName = resource.GetType().FullName!; + var resourceKey = resource.GetKeys().JoinAsString(","); + if (await PermissionChecker.IsGrantedAsync(context.User, requirement.PermissionName, resourceName, resourceKey)) + { + context.Succeed(requirement); + } + } +}