mirror of https://github.com/abpframework/abp.git
104 changed files with 1949 additions and 218 deletions
|
After Width: | Height: | Size: 154 KiB |
@ -0,0 +1,13 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.FlagIconCss |
||||
|
{ |
||||
|
public class FlagIconCssStyleContributor : BundleContributor |
||||
|
{ |
||||
|
public override void ConfigureBundle(BundleConfigurationContext context) |
||||
|
{ |
||||
|
context.Files.AddIfNotContains("/libs/flag-icon-css/css/flag-icon.min.css"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc.Abstractions; |
||||
|
using Volo.Abp.Aspects; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Features; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Features |
||||
|
{ |
||||
|
public class AbpFeatureActionFilter : IAsyncActionFilter, ITransientDependency |
||||
|
{ |
||||
|
private readonly IMethodInvocationFeatureCheckerService _methodInvocationAuthorizationService; |
||||
|
|
||||
|
public AbpFeatureActionFilter(IMethodInvocationFeatureCheckerService methodInvocationAuthorizationService) |
||||
|
{ |
||||
|
_methodInvocationAuthorizationService = methodInvocationAuthorizationService; |
||||
|
} |
||||
|
|
||||
|
public async Task OnActionExecutionAsync( |
||||
|
ActionExecutingContext context, |
||||
|
ActionExecutionDelegate next) |
||||
|
{ |
||||
|
if (!context.ActionDescriptor.IsControllerAction()) |
||||
|
{ |
||||
|
await next(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var methodInfo = context.ActionDescriptor.GetMethodInfo(); |
||||
|
|
||||
|
using (AbpCrossCuttingConcerns.Applying(context.Controller, AbpCrossCuttingConcerns.FeatureChecking)) |
||||
|
{ |
||||
|
await _methodInvocationAuthorizationService.CheckAsync( |
||||
|
new MethodInvocationFeatureCheckerContext(methodInfo) |
||||
|
); |
||||
|
|
||||
|
await next(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,8 +1,6 @@ |
|||||
using Volo.Abp.DependencyInjection; |
namespace Volo.Abp.Authorization.Permissions |
||||
|
|
||||
namespace Volo.Abp.Authorization.Permissions |
|
||||
{ |
{ |
||||
public interface IPermissionDefinitionProvider : ISingletonDependency |
public interface IPermissionDefinitionProvider |
||||
{ |
{ |
||||
void Define(IPermissionDefinitionContext context); |
void Define(IPermissionDefinitionContext context); |
||||
} |
} |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
using System.Threading.Tasks; |
using System.Threading.Tasks; |
||||
using Volo.Abp.DependencyInjection; |
|
||||
|
|
||||
namespace Volo.Abp.Authorization.Permissions |
namespace Volo.Abp.Authorization.Permissions |
||||
{ |
{ |
||||
public interface IPermissionValueProvider : ISingletonDependency |
public interface IPermissionValueProvider |
||||
{ |
{ |
||||
string Name { get; } |
string Name { get; } |
||||
|
|
||||
Task<PermissionValueProviderGrantInfo> CheckAsync(PermissionValueCheckContext context); |
//TODO: Rename to GetResult? (CheckAsync throws exception by naming convention)
|
||||
|
Task<PermissionGrantResult> CheckAsync(PermissionValueCheckContext context); |
||||
} |
} |
||||
} |
} |
||||
@ -1,20 +0,0 @@ |
|||||
using System.Security.Claims; |
|
||||
using System.Threading.Tasks; |
|
||||
|
|
||||
namespace Volo.Abp.Authorization.Permissions |
|
||||
{ |
|
||||
public static class PermissionCheckerExtensions |
|
||||
{ |
|
||||
public static async Task<bool> IsGrantedAsync(this IPermissionChecker permissionChecker, string name) |
|
||||
{ |
|
||||
return (await permissionChecker.CheckAsync(name)).IsGranted; |
|
||||
} |
|
||||
|
|
||||
public static async Task<bool> IsGrantedAsync(this IPermissionChecker permissionChecker, ClaimsPrincipal principal, string name) |
|
||||
{ |
|
||||
return (await permissionChecker.CheckAsync(principal, name)).IsGranted; |
|
||||
} |
|
||||
|
|
||||
//TODO: Add sync extensions
|
|
||||
} |
|
||||
} |
|
||||
@ -1,6 +1,8 @@ |
|||||
namespace Volo.Abp.Authorization.Permissions |
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Authorization.Permissions |
||||
{ |
{ |
||||
public abstract class PermissionDefinitionProvider : IPermissionDefinitionProvider |
public abstract class PermissionDefinitionProvider : IPermissionDefinitionProvider, ITransientDependency |
||||
{ |
{ |
||||
public abstract void Define(IPermissionDefinitionContext context); |
public abstract void Define(IPermissionDefinitionContext context); |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,9 @@ |
|||||
|
namespace Volo.Abp.Authorization.Permissions |
||||
|
{ |
||||
|
public enum PermissionGrantResult |
||||
|
{ |
||||
|
Undefined, |
||||
|
Granted, |
||||
|
Prohibited |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.Threading |
||||
|
{ |
||||
|
public static class TaskCache |
||||
|
{ |
||||
|
public static Task<bool> TrueResult { get; } |
||||
|
public static Task<bool> FalseResult { get; } |
||||
|
|
||||
|
static TaskCache() |
||||
|
{ |
||||
|
TrueResult = Task.FromResult(true); |
||||
|
FalseResult = Task.FromResult(false); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<AssemblyName>Volo.Abp.Features</AssemblyName> |
||||
|
<PackageId>Volo.Abp.Features</PackageId> |
||||
|
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
||||
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
||||
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
||||
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\Volo.Abp.Localization.Abstractions\Volo.Abp.Localization.Abstractions.csproj" /> |
||||
|
<ProjectReference Include="..\Volo.Abp.MultiTenancy.Abstractions\Volo.Abp.MultiTenancy.Abstractions.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,49 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpLocalizationAbstractionsModule), |
||||
|
typeof(AbpMultiTenancyAbstractionsModule) |
||||
|
)] |
||||
|
public class AbpFeaturesModule : AbpModule |
||||
|
{ |
||||
|
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
context.Services.OnRegistred(FeatureInterceptorRegistrar.RegisterIfNeeded); |
||||
|
AutoAddDefinitionProviders(context.Services); |
||||
|
} |
||||
|
|
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
context.Services.Configure<FeatureOptions>(options => |
||||
|
{ |
||||
|
options.ValueProviders.Add<DefaultValueFeatureValueProvider>(); |
||||
|
options.ValueProviders.Add<TenantFeatureValueProvider>(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private static void AutoAddDefinitionProviders(IServiceCollection services) |
||||
|
{ |
||||
|
var definitionProviders = new List<Type>(); |
||||
|
|
||||
|
services.OnRegistred(context => |
||||
|
{ |
||||
|
if (typeof(IFeatureDefinitionProvider).IsAssignableFrom(context.ImplementationType)) |
||||
|
{ |
||||
|
definitionProviders.Add(context.ImplementationType); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
services.Configure<FeatureOptions>(options => |
||||
|
{ |
||||
|
options.DefinitionProviders.AddIfNotContains(definitionProviders); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class DefaultValueFeatureValueProvider : FeatureValueProvider |
||||
|
{ |
||||
|
public const string ProviderName = "Default"; |
||||
|
|
||||
|
public override string Name => ProviderName; |
||||
|
|
||||
|
public DefaultValueFeatureValueProvider(IFeatureStore settingStore) |
||||
|
: base(settingStore) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public override Task<string> GetOrNullAsync(FeatureDefinition setting) |
||||
|
{ |
||||
|
return Task.FromResult(setting.DefaultValue); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
[AttributeUsage(AttributeTargets.Method)] |
||||
|
public class DisableFeatureCheckAttribute : Attribute |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,68 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class FeatureChecker : FeatureCheckerBase |
||||
|
{ |
||||
|
protected FeatureOptions Options { get; } |
||||
|
protected IServiceProvider ServiceProvider { get; } |
||||
|
protected IFeatureDefinitionManager FeatureDefinitionManager { get; } |
||||
|
protected List<IFeatureValueProvider> Providers => _providers.Value; |
||||
|
|
||||
|
private readonly Lazy<List<IFeatureValueProvider>> _providers; |
||||
|
|
||||
|
public FeatureChecker( |
||||
|
IOptions<FeatureOptions> options, |
||||
|
IServiceProvider serviceProvider, |
||||
|
IFeatureDefinitionManager featureDefinitionManager) |
||||
|
{ |
||||
|
ServiceProvider = serviceProvider; |
||||
|
FeatureDefinitionManager = featureDefinitionManager; |
||||
|
|
||||
|
Options = options.Value; |
||||
|
|
||||
|
_providers = new Lazy<List<IFeatureValueProvider>>( |
||||
|
() => Options |
||||
|
.ValueProviders |
||||
|
.Select(type => ServiceProvider.GetRequiredService(type) as IFeatureValueProvider) |
||||
|
.ToList(), |
||||
|
true |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public override async Task<string> GetOrNullAsync(string name) |
||||
|
{ |
||||
|
var featureDefinition = FeatureDefinitionManager.Get(name); |
||||
|
var providers = Enumerable |
||||
|
.Reverse(Providers); |
||||
|
|
||||
|
if (featureDefinition.AllowedProviders.Any()) |
||||
|
{ |
||||
|
providers = providers.Where(p => featureDefinition.AllowedProviders.Contains(p.Name)); |
||||
|
} |
||||
|
|
||||
|
return await GetOrNullValueFromProvidersAsync(providers, featureDefinition); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task<string> GetOrNullValueFromProvidersAsync( |
||||
|
IEnumerable<IFeatureValueProvider> providers, |
||||
|
FeatureDefinition feature) |
||||
|
{ |
||||
|
foreach (var provider in providers) |
||||
|
{ |
||||
|
var value = await provider.GetOrNullAsync(feature); |
||||
|
if (value != null) |
||||
|
{ |
||||
|
return value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public abstract class FeatureCheckerBase : IFeatureChecker, ITransientDependency |
||||
|
{ |
||||
|
public abstract Task<string> GetOrNullAsync(string name); |
||||
|
|
||||
|
public virtual async Task<bool> IsEnabledAsync(string name) |
||||
|
{ |
||||
|
var value = await GetOrNullAsync(name); |
||||
|
if (value == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
return bool.Parse(value); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
throw new AbpException( |
||||
|
$"The value '{value}' for the feature '{name}' should be a boolean, but was not!", |
||||
|
ex |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,143 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
using Volo.Abp.Authorization; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public static class FeatureCheckerExtensions |
||||
|
{ |
||||
|
public static async Task<T> GetAsync<T>( |
||||
|
[NotNull] this IFeatureChecker featureChecker, |
||||
|
[NotNull] string name, |
||||
|
T defaultValue = default) |
||||
|
where T : struct |
||||
|
{ |
||||
|
Check.NotNull(featureChecker, nameof(featureChecker)); |
||||
|
Check.NotNull(name, nameof(name)); |
||||
|
|
||||
|
var value = await featureChecker.GetOrNullAsync(name); |
||||
|
return value?.To<T>() ?? defaultValue; |
||||
|
} |
||||
|
|
||||
|
public static string GetOrNull( |
||||
|
[NotNull] this IFeatureChecker featureChecker, |
||||
|
[NotNull] string name) |
||||
|
{ |
||||
|
Check.NotNull(featureChecker, nameof(featureChecker)); |
||||
|
return AsyncHelper.RunSync(() => featureChecker.GetOrNullAsync(name)); |
||||
|
} |
||||
|
|
||||
|
public static T Get<T>( |
||||
|
[NotNull] this IFeatureChecker featureChecker, |
||||
|
[NotNull] string name, |
||||
|
T defaultValue = default) |
||||
|
where T : struct |
||||
|
{ |
||||
|
return AsyncHelper.RunSync(() => featureChecker.GetAsync(name, defaultValue)); |
||||
|
} |
||||
|
|
||||
|
public static bool IsEnabled( |
||||
|
[NotNull] this IFeatureChecker featureChecker, |
||||
|
[NotNull] string name) |
||||
|
{ |
||||
|
return AsyncHelper.RunSync(() => featureChecker.IsEnabledAsync(name)); |
||||
|
} |
||||
|
|
||||
|
public static async Task<bool> IsEnabledAsync(this IFeatureChecker featureChecker, bool requiresAll, params string[] featureNames) |
||||
|
{ |
||||
|
if (featureNames.IsNullOrEmpty()) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
if (requiresAll) |
||||
|
{ |
||||
|
foreach (var featureName in featureNames) |
||||
|
{ |
||||
|
if (!(await featureChecker.IsEnabledAsync(featureName))) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
foreach (var featureName in featureNames) |
||||
|
{ |
||||
|
if (await featureChecker.IsEnabledAsync(featureName)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static bool IsEnabled(this IFeatureChecker featureChecker, bool requiresAll, params string[] featureNames) |
||||
|
{ |
||||
|
return AsyncHelper.RunSync(() => featureChecker.IsEnabledAsync(requiresAll, featureNames)); |
||||
|
} |
||||
|
|
||||
|
public static async Task CheckEnabledAsync(this IFeatureChecker featureChecker, string featureName) |
||||
|
{ |
||||
|
if (!(await featureChecker.IsEnabledAsync(featureName))) |
||||
|
{ |
||||
|
throw new AbpAuthorizationException("Feature is not enabled: " + featureName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void CheckEnabled(this IFeatureChecker featureChecker, string featureName) |
||||
|
{ |
||||
|
if (!featureChecker.IsEnabled(featureName)) |
||||
|
{ |
||||
|
throw new AbpAuthorizationException("Feature is not enabled: " + featureName); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static async Task CheckEnabledAsync(this IFeatureChecker featureChecker, bool requiresAll, params string[] featureNames) |
||||
|
{ |
||||
|
if (featureNames.IsNullOrEmpty()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (requiresAll) |
||||
|
{ |
||||
|
foreach (var featureName in featureNames) |
||||
|
{ |
||||
|
if (!(await featureChecker.IsEnabledAsync(featureName))) |
||||
|
{ |
||||
|
throw new AbpAuthorizationException( |
||||
|
"Required features are not enabled. All of these features must be enabled: " + |
||||
|
string.Join(", ", featureNames) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
foreach (var featureName in featureNames) |
||||
|
{ |
||||
|
if (await featureChecker.IsEnabledAsync(featureName)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
throw new AbpAuthorizationException( |
||||
|
"Required features are not enabled. At least one of these features must be enabled: " + |
||||
|
string.Join(", ", featureNames) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void CheckEnabled(this IFeatureChecker featureChecker, bool requiresAll, params string[] featureNames) |
||||
|
{ |
||||
|
AsyncHelper.RunSync(() => featureChecker.CheckEnabledAsync(requiresAll, featureNames)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,164 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
using System.Linq; |
||||
|
using JetBrains.Annotations; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class FeatureDefinition |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Unique name of the feature.
|
||||
|
/// </summary>
|
||||
|
[NotNull] |
||||
|
public string Name { get; } |
||||
|
|
||||
|
[NotNull] |
||||
|
public ILocalizableString DisplayName |
||||
|
{ |
||||
|
get => _displayName; |
||||
|
set => _displayName = Check.NotNull(value, nameof(value)); |
||||
|
} |
||||
|
private ILocalizableString _displayName; |
||||
|
|
||||
|
[CanBeNull] |
||||
|
public ILocalizableString Description { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Parent of this feature, if one exists.
|
||||
|
/// If set, this feature can be enabled only if the parent is enabled.
|
||||
|
/// </summary>
|
||||
|
public FeatureDefinition Parent { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// List of child features.
|
||||
|
/// </summary>
|
||||
|
public IReadOnlyList<FeatureDefinition> Children => _children.ToImmutableList(); |
||||
|
private readonly List<FeatureDefinition> _children; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Default value of the feature.
|
||||
|
/// </summary>
|
||||
|
[CanBeNull] |
||||
|
public string DefaultValue { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Can clients see this feature and it's value.
|
||||
|
/// Default: true.
|
||||
|
/// </summary>
|
||||
|
public bool IsVisibleToClients { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A list of allowed providers to get/set value of this feature.
|
||||
|
/// An empty list indicates that all providers are allowed.
|
||||
|
/// </summary>
|
||||
|
public List<string> AllowedProviders { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets/sets a key-value on the <see cref="Properties"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="name">Name of the property</param>
|
||||
|
/// <returns>
|
||||
|
/// Returns the value in the <see cref="Properties"/> dictionary by given <see cref="name"/>.
|
||||
|
/// Returns null if given <see cref="name"/> is not present in the <see cref="Properties"/> dictionary.
|
||||
|
/// </returns>
|
||||
|
public object this[string name] |
||||
|
{ |
||||
|
get => Properties.GetOrDefault(name); |
||||
|
set => Properties[name] = value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Can be used to get/set custom properties for this feature.
|
||||
|
/// </summary>
|
||||
|
[NotNull] |
||||
|
public Dictionary<string, object> Properties { get; } |
||||
|
|
||||
|
//TODO: Implement input type like old ABP!
|
||||
|
|
||||
|
public FeatureDefinition( |
||||
|
string name, |
||||
|
string defaultValue = null, |
||||
|
ILocalizableString displayName = null, |
||||
|
ILocalizableString description = null, |
||||
|
bool isVisibleToClients = true) |
||||
|
{ |
||||
|
Name = name; |
||||
|
DefaultValue = defaultValue; |
||||
|
IsVisibleToClients = isVisibleToClients; |
||||
|
DisplayName = displayName ?? new FixedLocalizableString(name); |
||||
|
Description = description; |
||||
|
|
||||
|
Properties = new Dictionary<string, object>(); |
||||
|
AllowedProviders = new List<string>(); |
||||
|
_children = new List<FeatureDefinition>(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets a property in the <see cref="Properties"/> dictionary.
|
||||
|
/// This is a shortcut for nested calls on this object.
|
||||
|
/// </summary>
|
||||
|
public virtual FeatureDefinition WithProperty(string key, object value) |
||||
|
{ |
||||
|
Properties[key] = value; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets a property in the <see cref="Properties"/> dictionary.
|
||||
|
/// This is a shortcut for nested calls on this object.
|
||||
|
/// </summary>
|
||||
|
public virtual FeatureDefinition WithProviders(params string[] providers) |
||||
|
{ |
||||
|
if (!providers.IsNullOrEmpty()) |
||||
|
{ |
||||
|
AllowedProviders.AddRange(providers); |
||||
|
} |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Adds a child feature.
|
||||
|
/// </summary>
|
||||
|
/// <returns>Returns a newly created child feature</returns>
|
||||
|
public FeatureDefinition CreateChild( |
||||
|
string name, |
||||
|
string defaultValue = null, |
||||
|
ILocalizableString displayName = null, |
||||
|
ILocalizableString description = null, |
||||
|
bool isVisibleToClients = true) |
||||
|
{ |
||||
|
var feature = new FeatureDefinition( |
||||
|
name, |
||||
|
defaultValue, |
||||
|
displayName, |
||||
|
description, |
||||
|
isVisibleToClients) |
||||
|
{ |
||||
|
Parent = this |
||||
|
}; |
||||
|
|
||||
|
_children.Add(feature); |
||||
|
return feature; |
||||
|
} |
||||
|
|
||||
|
public void RemoveChild(string name) |
||||
|
{ |
||||
|
var featureToRemove = _children.FirstOrDefault(f => f.Name == name); |
||||
|
if (featureToRemove == null) |
||||
|
{ |
||||
|
throw new AbpException($"Could not find a feature named '{name}' in the Children of this feature '{Name}'."); |
||||
|
} |
||||
|
|
||||
|
featureToRemove.Parent = null; |
||||
|
_children.Remove(featureToRemove); |
||||
|
} |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return $"[{nameof(FeatureDefinition)}: {Name}]"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class FeatureDefinitionContext : IFeatureDefinitionContext |
||||
|
{ |
||||
|
internal Dictionary<string, FeatureGroupDefinition> Groups { get; } |
||||
|
|
||||
|
public FeatureDefinitionContext() |
||||
|
{ |
||||
|
Groups = new Dictionary<string, FeatureGroupDefinition>(); |
||||
|
} |
||||
|
|
||||
|
public FeatureGroupDefinition AddGroup(string name, ILocalizableString displayName = null) |
||||
|
{ |
||||
|
Check.NotNull(name, nameof(name)); |
||||
|
|
||||
|
if (Groups.ContainsKey(name)) |
||||
|
{ |
||||
|
throw new AbpException($"There is already an existing permission group with name: {name}"); |
||||
|
} |
||||
|
|
||||
|
return Groups[name] = new FeatureGroupDefinition(name, displayName); |
||||
|
} |
||||
|
|
||||
|
public FeatureGroupDefinition GetGroupOrNull(string name) |
||||
|
{ |
||||
|
Check.NotNull(name, nameof(name)); |
||||
|
|
||||
|
if (!Groups.ContainsKey(name)) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return Groups[name]; |
||||
|
} |
||||
|
|
||||
|
public void RemoveGroup(string name) |
||||
|
{ |
||||
|
Check.NotNull(name, nameof(name)); |
||||
|
|
||||
|
if (!Groups.ContainsKey(name)) |
||||
|
{ |
||||
|
throw new AbpException($"Undefined feature group: '{name}'."); |
||||
|
} |
||||
|
|
||||
|
Groups.Remove(name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,117 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
using System.Linq; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class FeatureDefinitionManager : IFeatureDefinitionManager, ISingletonDependency |
||||
|
{ |
||||
|
protected IDictionary<string, FeatureGroupDefinition> FeatureGroupDefinitions => _lazyFeatureGroupDefinitions.Value; |
||||
|
private readonly Lazy<Dictionary<string, FeatureGroupDefinition>> _lazyFeatureGroupDefinitions; |
||||
|
|
||||
|
protected IDictionary<string, FeatureDefinition> FeatureDefinitions => _lazyFeatureDefinitions.Value; |
||||
|
private readonly Lazy<Dictionary<string, FeatureDefinition>> _lazyFeatureDefinitions; |
||||
|
|
||||
|
protected FeatureOptions Options { get; } |
||||
|
|
||||
|
private readonly IServiceProvider _serviceProvider; |
||||
|
|
||||
|
public FeatureDefinitionManager( |
||||
|
IOptions<FeatureOptions> options, |
||||
|
IServiceProvider serviceProvider) |
||||
|
{ |
||||
|
_serviceProvider = serviceProvider; |
||||
|
Options = options.Value; |
||||
|
|
||||
|
_lazyFeatureDefinitions = new Lazy<Dictionary<string, FeatureDefinition>>( |
||||
|
CreateFeatureDefinitions, |
||||
|
isThreadSafe: true |
||||
|
); |
||||
|
|
||||
|
_lazyFeatureGroupDefinitions = new Lazy<Dictionary<string, FeatureGroupDefinition>>( |
||||
|
CreateFeatureGroupDefinitions, |
||||
|
isThreadSafe:true |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public virtual FeatureDefinition Get(string name) |
||||
|
{ |
||||
|
Check.NotNull(name, nameof(name)); |
||||
|
|
||||
|
var feature = GetOrNull(name); |
||||
|
|
||||
|
if (feature == null) |
||||
|
{ |
||||
|
throw new AbpException("Undefined feature: " + name); |
||||
|
} |
||||
|
|
||||
|
return feature; |
||||
|
} |
||||
|
|
||||
|
public virtual IReadOnlyList<FeatureDefinition> GetAll() |
||||
|
{ |
||||
|
return FeatureDefinitions.Values.ToImmutableList(); |
||||
|
} |
||||
|
|
||||
|
public virtual FeatureDefinition GetOrNull(string name) |
||||
|
{ |
||||
|
return FeatureDefinitions.GetOrDefault(name); |
||||
|
} |
||||
|
|
||||
|
protected virtual Dictionary<string, FeatureDefinition> CreateFeatureDefinitions() |
||||
|
{ |
||||
|
var features = new Dictionary<string, FeatureDefinition>(); |
||||
|
|
||||
|
foreach (var groupDefinition in FeatureGroupDefinitions.Values) |
||||
|
{ |
||||
|
foreach (var feature in groupDefinition.Features) |
||||
|
{ |
||||
|
AddFeatureToDictionaryRecursively(features, feature); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return features; |
||||
|
} |
||||
|
|
||||
|
protected virtual void AddFeatureToDictionaryRecursively( |
||||
|
Dictionary<string, FeatureDefinition> features, |
||||
|
FeatureDefinition feature) |
||||
|
{ |
||||
|
if (features.ContainsKey(feature.Name)) |
||||
|
{ |
||||
|
throw new AbpException("Duplicate feature name: " + feature.Name); |
||||
|
} |
||||
|
|
||||
|
features[feature.Name] = feature; |
||||
|
|
||||
|
foreach (var child in feature.Children) |
||||
|
{ |
||||
|
AddFeatureToDictionaryRecursively(features, child); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual Dictionary<string, FeatureGroupDefinition> CreateFeatureGroupDefinitions() |
||||
|
{ |
||||
|
var context = new FeatureDefinitionContext(); |
||||
|
|
||||
|
using (var scope = _serviceProvider.CreateScope()) |
||||
|
{ |
||||
|
var providers = Options |
||||
|
.DefinitionProviders |
||||
|
.Select(p => scope.ServiceProvider.GetRequiredService(p) as IFeatureDefinitionProvider) |
||||
|
.ToList(); |
||||
|
|
||||
|
foreach (var provider in providers) |
||||
|
{ |
||||
|
provider.Define(context); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return context.Groups; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public abstract class FeatureDefinitionProvider : IFeatureDefinitionProvider, ISingletonDependency |
||||
|
{ |
||||
|
public abstract void Define(IFeatureDefinitionContext context); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,108 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class FeatureGroupDefinition |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Unique name of the group.
|
||||
|
/// </summary>
|
||||
|
public string Name { get; } |
||||
|
|
||||
|
public Dictionary<string, object> Properties { get; } |
||||
|
|
||||
|
public ILocalizableString DisplayName |
||||
|
{ |
||||
|
get => _displayName; |
||||
|
set => _displayName = Check.NotNull(value, nameof(value)); |
||||
|
} |
||||
|
private ILocalizableString _displayName; |
||||
|
|
||||
|
public IReadOnlyList<FeatureDefinition> Features => _features.ToImmutableList(); |
||||
|
private readonly List<FeatureDefinition> _features; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets/sets a key-value on the <see cref="Properties"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="name">Name of the property</param>
|
||||
|
/// <returns>
|
||||
|
/// Returns the value in the <see cref="Properties"/> dictionary by given <see cref="name"/>.
|
||||
|
/// Returns null if given <see cref="name"/> is not present in the <see cref="Properties"/> dictionary.
|
||||
|
/// </returns>
|
||||
|
public object this[string name] |
||||
|
{ |
||||
|
get => Properties.GetOrDefault(name); |
||||
|
set => Properties[name] = value; |
||||
|
} |
||||
|
|
||||
|
protected internal FeatureGroupDefinition( |
||||
|
string name, |
||||
|
ILocalizableString displayName = null) |
||||
|
{ |
||||
|
Name = name; |
||||
|
DisplayName = displayName ?? new FixedLocalizableString(Name); |
||||
|
|
||||
|
Properties = new Dictionary<string, object>(); |
||||
|
_features = new List<FeatureDefinition>(); |
||||
|
} |
||||
|
|
||||
|
public virtual FeatureDefinition AddFeature( |
||||
|
string name, |
||||
|
string defaultValue = null, |
||||
|
ILocalizableString displayName = null, |
||||
|
ILocalizableString description = null, |
||||
|
bool isVisibleToClients = true) |
||||
|
{ |
||||
|
var feature = new FeatureDefinition( |
||||
|
name, |
||||
|
defaultValue, |
||||
|
displayName, |
||||
|
description, |
||||
|
isVisibleToClients |
||||
|
); |
||||
|
|
||||
|
_features.Add(feature); |
||||
|
|
||||
|
return feature; |
||||
|
} |
||||
|
|
||||
|
public virtual List<FeatureDefinition> GetFeaturesWithChildren() |
||||
|
{ |
||||
|
var features = new List<FeatureDefinition>(); |
||||
|
|
||||
|
foreach (var feature in _features) |
||||
|
{ |
||||
|
AddFeatureToListRecursively(features, feature); |
||||
|
} |
||||
|
|
||||
|
return features; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets a property in the <see cref="Properties"/> dictionary.
|
||||
|
/// This is a shortcut for nested calls on this object.
|
||||
|
/// </summary>
|
||||
|
public virtual FeatureGroupDefinition WithProperty(string key, object value) |
||||
|
{ |
||||
|
Properties[key] = value; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
private void AddFeatureToListRecursively(List<FeatureDefinition> features, FeatureDefinition feature) |
||||
|
{ |
||||
|
features.Add(feature); |
||||
|
|
||||
|
foreach (var child in feature.Children) |
||||
|
{ |
||||
|
AddFeatureToListRecursively(features, child); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return $"[{nameof(FeatureGroupDefinition)} {Name}]"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,56 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Aspects; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.DynamicProxy; |
||||
|
using Volo.Abp.Threading; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class FeatureInterceptor : AbpInterceptor, ITransientDependency |
||||
|
{ |
||||
|
private readonly IMethodInvocationFeatureCheckerService _methodInvocationAuthorizationService; |
||||
|
|
||||
|
public FeatureInterceptor( |
||||
|
IMethodInvocationFeatureCheckerService methodInvocationAuthorizationService) |
||||
|
{ |
||||
|
_methodInvocationAuthorizationService = methodInvocationAuthorizationService; |
||||
|
} |
||||
|
|
||||
|
public override void Intercept(IAbpMethodInvocation invocation) |
||||
|
{ |
||||
|
if (AbpCrossCuttingConcerns.IsApplied( |
||||
|
invocation.TargetObject, |
||||
|
AbpCrossCuttingConcerns.FeatureChecking)) |
||||
|
{ |
||||
|
invocation.Proceed(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
AsyncHelper.RunSync(() => CheckFeaturesAsync(invocation)); |
||||
|
invocation.Proceed(); |
||||
|
} |
||||
|
|
||||
|
public override async Task InterceptAsync(IAbpMethodInvocation invocation) |
||||
|
{ |
||||
|
if (AbpCrossCuttingConcerns.IsApplied( |
||||
|
invocation.TargetObject, |
||||
|
AbpCrossCuttingConcerns.FeatureChecking)) |
||||
|
{ |
||||
|
await invocation.ProceedAsync(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
AsyncHelper.RunSync(() => CheckFeaturesAsync(invocation)); |
||||
|
await invocation.ProceedAsync(); |
||||
|
} |
||||
|
|
||||
|
protected virtual Task CheckFeaturesAsync(IAbpMethodInvocation invocation) |
||||
|
{ |
||||
|
return _methodInvocationAuthorizationService.CheckAsync( |
||||
|
new MethodInvocationFeatureCheckerContext( |
||||
|
invocation.Method |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public static class FeatureInterceptorRegistrar |
||||
|
{ |
||||
|
public static void RegisterIfNeeded(IOnServiceRegistredContext context) |
||||
|
{ |
||||
|
if (ShouldIntercept(context.ImplementationType)) |
||||
|
{ |
||||
|
context.Interceptors.TryAdd<FeatureInterceptor>(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static bool ShouldIntercept(Type type) |
||||
|
{ |
||||
|
return type.IsDefined(typeof(RequiresFeatureAttribute), true) || |
||||
|
AnyMethodRequiresFeatureAttribute(type); |
||||
|
} |
||||
|
|
||||
|
private static bool AnyMethodRequiresFeatureAttribute(Type implementationType) |
||||
|
{ |
||||
|
return implementationType |
||||
|
.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) |
||||
|
.Any(HasRequiresFeatureAttribute); |
||||
|
} |
||||
|
|
||||
|
private static bool HasRequiresFeatureAttribute(MemberInfo methodInfo) |
||||
|
{ |
||||
|
return methodInfo.IsDefined(typeof(RequiresFeatureAttribute), true); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using Volo.Abp.Collections; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class FeatureOptions |
||||
|
{ |
||||
|
public ITypeList<IFeatureDefinitionProvider> DefinitionProviders { get; } |
||||
|
|
||||
|
public ITypeList<IFeatureValueProvider> ValueProviders { get; } |
||||
|
|
||||
|
public FeatureOptions() |
||||
|
{ |
||||
|
DefinitionProviders = new TypeList<IFeatureDefinitionProvider>(); |
||||
|
ValueProviders = new TypeList<IFeatureValueProvider>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
[Serializable] |
||||
|
public class FeatureValue : NameValue |
||||
|
{ |
||||
|
public FeatureValue() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public FeatureValue(string name, string value) |
||||
|
{ |
||||
|
Name = name; |
||||
|
Value = value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public abstract class FeatureValueProvider : IFeatureValueProvider, ISingletonDependency |
||||
|
{ |
||||
|
public abstract string Name { get; } |
||||
|
|
||||
|
protected IFeatureStore FeatureStore { get; } |
||||
|
|
||||
|
protected FeatureValueProvider(IFeatureStore featureStore) |
||||
|
{ |
||||
|
FeatureStore = featureStore; |
||||
|
} |
||||
|
|
||||
|
public abstract Task<string> GetOrNullAsync(FeatureDefinition feature); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public interface IFeatureChecker |
||||
|
{ |
||||
|
Task<string> GetOrNullAsync([NotNull] string name); |
||||
|
|
||||
|
Task<bool> IsEnabledAsync(string name); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public interface IFeatureDefinitionContext |
||||
|
{ |
||||
|
FeatureGroupDefinition AddGroup([NotNull] string name, ILocalizableString displayName = null); |
||||
|
|
||||
|
FeatureGroupDefinition GetGroupOrNull(string name); |
||||
|
|
||||
|
void RemoveGroup(string name); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public interface IFeatureDefinitionManager |
||||
|
{ |
||||
|
[NotNull] |
||||
|
FeatureDefinition Get([NotNull] string name); |
||||
|
|
||||
|
IReadOnlyList<FeatureDefinition> GetAll(); |
||||
|
|
||||
|
FeatureDefinition GetOrNull(string name); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public interface IFeatureDefinitionProvider |
||||
|
{ |
||||
|
void Define(IFeatureDefinitionContext context); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public interface IFeatureStore |
||||
|
{ |
||||
|
Task<string> GetOrNullAsync( |
||||
|
[NotNull] string name, |
||||
|
[CanBeNull] string providerName, |
||||
|
[CanBeNull] string providerKey |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public interface IFeatureValueProvider |
||||
|
{ |
||||
|
string Name { get; } |
||||
|
|
||||
|
Task<string> GetOrNullAsync([NotNull] FeatureDefinition feature); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public interface IMethodInvocationFeatureCheckerService |
||||
|
{ |
||||
|
Task CheckAsync( |
||||
|
MethodInvocationFeatureCheckerContext context |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using System.Reflection; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class MethodInvocationFeatureCheckerContext |
||||
|
{ |
||||
|
public MethodInfo Method { get; } |
||||
|
|
||||
|
public MethodInvocationFeatureCheckerContext(MethodInfo method) |
||||
|
{ |
||||
|
Method = method; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class MethodInvocationFeatureCheckerService : IMethodInvocationFeatureCheckerService, ITransientDependency |
||||
|
{ |
||||
|
private readonly IFeatureChecker _featureChecker; |
||||
|
|
||||
|
public MethodInvocationFeatureCheckerService( |
||||
|
IFeatureChecker featureChecker) |
||||
|
{ |
||||
|
_featureChecker = featureChecker; |
||||
|
} |
||||
|
|
||||
|
public async Task CheckAsync(MethodInvocationFeatureCheckerContext context) |
||||
|
{ |
||||
|
if (IsFeatureCheckDisabled(context)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
foreach (var requiresFeatureAttribute in GetRequiredFeatureAttributes(context)) |
||||
|
{ |
||||
|
await _featureChecker.CheckEnabledAsync(requiresFeatureAttribute.RequiresAll, requiresFeatureAttribute.Features); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual bool IsFeatureCheckDisabled(MethodInvocationFeatureCheckerContext context) |
||||
|
{ |
||||
|
return context.Method |
||||
|
.GetCustomAttributes(true) |
||||
|
.OfType<DisableFeatureCheckAttribute>() |
||||
|
.Any(); |
||||
|
} |
||||
|
|
||||
|
protected virtual RequiresFeatureAttribute[] GetRequiredFeatureAttributes(MethodInvocationFeatureCheckerContext context) |
||||
|
{ |
||||
|
var classAttributes = context.Method.DeclaringType |
||||
|
.GetCustomAttributes(true) |
||||
|
.OfType<RequiresFeatureAttribute>(); |
||||
|
|
||||
|
var methodAttributes = context.Method |
||||
|
.GetCustomAttributes(true) |
||||
|
.OfType<RequiresFeatureAttribute>(); |
||||
|
|
||||
|
return classAttributes.Union(methodAttributes).ToArray(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
[Dependency(TryRegister = true)] |
||||
|
public class NullFeatureStore : IFeatureStore, ISingletonDependency |
||||
|
{ |
||||
|
public ILogger<NullFeatureStore> Logger { get; set; } |
||||
|
|
||||
|
public NullFeatureStore() |
||||
|
{ |
||||
|
Logger = NullLogger<NullFeatureStore>.Instance; |
||||
|
} |
||||
|
|
||||
|
public Task<string> GetOrNullAsync(string name, string providerName, string providerKey) |
||||
|
{ |
||||
|
return Task.FromResult((string) null); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This attribute can be used on a class/method to declare that given class/method is available
|
||||
|
/// only if required feature(s) are enabled.
|
||||
|
/// </summary>
|
||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] |
||||
|
public class RequiresFeatureAttribute : Attribute |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A list of features to be checked if they are enabled.
|
||||
|
/// </summary>
|
||||
|
public string[] Features { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// If this property is set to true, all of the <see cref="Features"/> must be enabled.
|
||||
|
/// If it's false, at least one of the <see cref="Features"/> must be enabled.
|
||||
|
/// Default: false.
|
||||
|
/// </summary>
|
||||
|
public bool RequiresAll { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new instance of <see cref="RequiresFeatureAttribute"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="features">A list of features to be checked if they are enabled</param>
|
||||
|
public RequiresFeatureAttribute(params string[] features) |
||||
|
{ |
||||
|
Features = features ?? Array.Empty<string>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class TenantFeatureValueProvider : FeatureValueProvider |
||||
|
{ |
||||
|
public const string ProviderName = "Tenant"; |
||||
|
|
||||
|
public override string Name => ProviderName; |
||||
|
|
||||
|
protected ICurrentTenant CurrentTenant { get; } |
||||
|
|
||||
|
public TenantFeatureValueProvider(IFeatureStore featureStore, ICurrentTenant currentTenant) |
||||
|
: base(featureStore) |
||||
|
{ |
||||
|
CurrentTenant = currentTenant; |
||||
|
} |
||||
|
|
||||
|
public override async Task<string> GetOrNullAsync(FeatureDefinition feature) |
||||
|
{ |
||||
|
return await FeatureStore.GetOrNullAsync(feature.Name, Name, CurrentTenant.Id?.ToString()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Features; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Features |
||||
|
{ |
||||
|
public class FakeFeatureChecker : FeatureCheckerBase |
||||
|
{ |
||||
|
public override Task<string> GetOrNullAsync(string name) |
||||
|
{ |
||||
|
return Task.FromResult(GetOrNull(name)); |
||||
|
} |
||||
|
|
||||
|
private static string GetOrNull(string name) |
||||
|
{ |
||||
|
switch (name) |
||||
|
{ |
||||
|
case "AllowedFeature": |
||||
|
return true.ToString(); |
||||
|
case "NotAllowedFeature": |
||||
|
return null; //or false, doesn't matter
|
||||
|
} |
||||
|
|
||||
|
throw new ApplicationException($"Unknown feature: '{name}'"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Volo.Abp.Features; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Features |
||||
|
{ |
||||
|
[Route("api/feature-test")] |
||||
|
public class FeatureTestController : AbpController |
||||
|
{ |
||||
|
[HttpGet] |
||||
|
[Route("allowed-feature")] |
||||
|
[RequiresFeature("AllowedFeature")] |
||||
|
public Task AllowedFeatureAsync() |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("not-allowed-feature")] |
||||
|
[RequiresFeature("NotAllowedFeature")] |
||||
|
public void NotAllowedFeature() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("no-feature")] |
||||
|
public int NoFeature() |
||||
|
{ |
||||
|
return 42; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using System.Net; |
||||
|
using System.Threading.Tasks; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Features |
||||
|
{ |
||||
|
public class FeatureTestController_Tests : AspNetCoreMvcTestBase |
||||
|
{ |
||||
|
[Fact] |
||||
|
public async Task Should_Allow_Enabled_Features() |
||||
|
{ |
||||
|
await GetResponseAsStringAsync( |
||||
|
"/api/feature-test/allowed-feature" |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Not_Allow_Not_Enabled_Features() |
||||
|
{ |
||||
|
await GetResponseAsStringAsync( |
||||
|
"/api/feature-test/not-allowed-feature", |
||||
|
HttpStatusCode.Unauthorized |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Allow_Actions_With_No_Feature() |
||||
|
{ |
||||
|
await GetResponseAsStringAsync( |
||||
|
"/api/feature-test/no-feature" |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netcoreapp2.2</TargetFramework> |
||||
|
<LangVersion>latest</LangVersion> |
||||
|
<AssemblyName>Volo.Abp.Features.Tests</AssemblyName> |
||||
|
<PackageId>Volo.Abp.Features.Tests</PackageId> |
||||
|
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> |
||||
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
||||
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
||||
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> |
||||
|
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Volo.Abp.Features\Volo.Abp.Features.csproj" /> |
||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,18 @@ |
|||||
|
using Volo.Abp.Autofac; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpFeaturesModule), |
||||
|
typeof(AbpTestBaseModule), |
||||
|
typeof(AbpAutofacModule) |
||||
|
)] |
||||
|
public class AbpFeaturesTestModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
[RequiresFeature("BooleanTestFeature1")] |
||||
|
public class ClassFeatureTestService : ITransientDependency |
||||
|
{ |
||||
|
/* Since this class is used with the class reference, |
||||
|
* need to virtual keywords, otherwise dynamic proxy can not work. |
||||
|
*/ |
||||
|
|
||||
|
[RequiresFeature("BooleanTestFeature2")] |
||||
|
public virtual int Feature2() |
||||
|
{ |
||||
|
return 42; |
||||
|
} |
||||
|
|
||||
|
public virtual void NoAdditionalFeature() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,53 @@ |
|||||
|
using Shouldly; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class FeatureChecker_Tests : FeatureTestBase |
||||
|
{ |
||||
|
private readonly IFeatureChecker _featureChecker; |
||||
|
private readonly ICurrentTenant _currentTenant; |
||||
|
|
||||
|
public FeatureChecker_Tests() |
||||
|
{ |
||||
|
_featureChecker = GetRequiredService<IFeatureChecker>(); |
||||
|
_currentTenant = GetRequiredService<ICurrentTenant>(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task IsEnabledAsync() |
||||
|
{ |
||||
|
//Tenant is unknown
|
||||
|
(await _featureChecker.IsEnabledAsync("BooleanTestFeature1")).ShouldBeFalse(); |
||||
|
|
||||
|
using (_currentTenant.Change(TestFeatureStore.Tenant1Id)) |
||||
|
{ |
||||
|
(await _featureChecker.IsEnabledAsync("BooleanTestFeature1")).ShouldBeTrue(); |
||||
|
} |
||||
|
|
||||
|
using (_currentTenant.Change(TestFeatureStore.Tenant2Id)) |
||||
|
{ |
||||
|
(await _featureChecker.IsEnabledAsync("BooleanTestFeature1")).ShouldBeFalse(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task GetOrNullAsync() |
||||
|
{ |
||||
|
//Tenant is unknown
|
||||
|
(await _featureChecker.GetOrNullAsync("IntegerTestFeature1")).ShouldBe("1"); |
||||
|
|
||||
|
using (_currentTenant.Change(TestFeatureStore.Tenant1Id)) |
||||
|
{ |
||||
|
(await _featureChecker.GetOrNullAsync("IntegerTestFeature1")).ShouldBe("1"); |
||||
|
} |
||||
|
|
||||
|
using (_currentTenant.Change(TestFeatureStore.Tenant2Id)) |
||||
|
{ |
||||
|
(await _featureChecker.GetOrNullAsync("IntegerTestFeature1")).ShouldBe("34"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
using Shouldly; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class FeatureDefinitionManager_Tests : FeatureTestBase |
||||
|
{ |
||||
|
private readonly IFeatureDefinitionManager _featureDefinitionManager; |
||||
|
|
||||
|
public FeatureDefinitionManager_Tests() |
||||
|
{ |
||||
|
_featureDefinitionManager = GetRequiredService<IFeatureDefinitionManager>(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Get_Defined_Features() |
||||
|
{ |
||||
|
_featureDefinitionManager.GetOrNull("BooleanTestFeature1").ShouldNotBeNull(); |
||||
|
_featureDefinitionManager.Get("BooleanTestFeature1").Name.ShouldBe("BooleanTestFeature1"); |
||||
|
|
||||
|
_featureDefinitionManager.GetOrNull("IntegerTestFeature1").ShouldNotBeNull(); |
||||
|
_featureDefinitionManager.Get("IntegerTestFeature1").Name.ShouldBe("IntegerTestFeature1"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Not_Get_Undefined_Features() |
||||
|
{ |
||||
|
_featureDefinitionManager.GetOrNull("UndefinedFeature").ShouldBeNull(); |
||||
|
Assert.Throws<AbpException>(() => |
||||
|
{ |
||||
|
_featureDefinitionManager.Get("UndefinedFeature"); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,76 @@ |
|||||
|
using Shouldly; |
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Authorization; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class FeatureInterceptor_Tests : FeatureTestBase |
||||
|
{ |
||||
|
private readonly ClassFeatureTestService _classFeatureTestService; |
||||
|
private readonly IMethodFeatureTestService _methodFeatureTestService; |
||||
|
private readonly ICurrentTenant _currentTenant; |
||||
|
|
||||
|
public FeatureInterceptor_Tests() |
||||
|
{ |
||||
|
_classFeatureTestService = GetRequiredService<ClassFeatureTestService>(); |
||||
|
_methodFeatureTestService = GetRequiredService<IMethodFeatureTestService>(); |
||||
|
_currentTenant = GetRequiredService<ICurrentTenant>(); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(null)] //Features were not enabled for null tenantid
|
||||
|
[InlineData(TestFeatureStore.Tenant2IdValue)] //Features were not enabled for Tenant 2
|
||||
|
public async Task Should_Not_Allow_To_Method_Calls_If_Related_Features_Were_Not_Enabled(string tenantIdValue) |
||||
|
{ |
||||
|
using (_currentTenant.Change(ParseNullableGuid(tenantIdValue))) |
||||
|
{ |
||||
|
Assert.Throws<AbpAuthorizationException>(() => |
||||
|
{ |
||||
|
_classFeatureTestService.NoAdditionalFeature(); |
||||
|
}); |
||||
|
|
||||
|
Assert.Throws<AbpAuthorizationException>(() => |
||||
|
{ |
||||
|
_classFeatureTestService.Feature2(); |
||||
|
}); |
||||
|
|
||||
|
await Assert.ThrowsAsync<AbpAuthorizationException>(async () => |
||||
|
{ |
||||
|
await _methodFeatureTestService.Feature1Async(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Allow_To_Method_Calls_If_Related_Features_Were_Enabled() |
||||
|
{ |
||||
|
//Features were enabled for Tenant 1
|
||||
|
using (_currentTenant.Change(TestFeatureStore.Tenant1Id)) |
||||
|
{ |
||||
|
_classFeatureTestService.NoAdditionalFeature(); |
||||
|
_classFeatureTestService.Feature2().ShouldBe(42); |
||||
|
(await _methodFeatureTestService.Feature1Async()).ShouldBe(42); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(null)] |
||||
|
[InlineData(TestFeatureStore.Tenant1IdValue)] |
||||
|
[InlineData(TestFeatureStore.Tenant2IdValue)] |
||||
|
public async Task Should_Allow_To_Method_Calls_For_Those_Have_No_RequiresFeature_Attributes(string tenantIdValue) |
||||
|
{ |
||||
|
using (_currentTenant.Change(ParseNullableGuid(tenantIdValue))) |
||||
|
{ |
||||
|
await _methodFeatureTestService.NonFeatureAsync(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static Guid? ParseNullableGuid(string tenantIdValue) |
||||
|
{ |
||||
|
return tenantIdValue == null ? (Guid?)null : new Guid(tenantIdValue); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class FeatureTestBase : AbpIntegratedTest<AbpFeaturesTestModule> |
||||
|
{ |
||||
|
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
||||
|
{ |
||||
|
options.UseAutofac(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public interface IMethodFeatureTestService |
||||
|
{ |
||||
|
Task<int> Feature1Async(); |
||||
|
|
||||
|
Task NonFeatureAsync(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class MethodFeatureTestService : ITransientDependency, IMethodFeatureTestService |
||||
|
{ |
||||
|
/* Since this class is used over an interface (IMethodFeatureTestService), |
||||
|
* no need to virtual keywords, dynamic proxy can work. |
||||
|
*/ |
||||
|
|
||||
|
[RequiresFeature("BooleanTestFeature1")] |
||||
|
public Task<int> Feature1Async() |
||||
|
{ |
||||
|
return Task.FromResult(42); |
||||
|
} |
||||
|
|
||||
|
public Task NonFeatureAsync() |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class TestFeatureDefinitionProvider : FeatureDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(IFeatureDefinitionContext context) |
||||
|
{ |
||||
|
var group = context.AddGroup("Test Group"); |
||||
|
group.AddFeature("BooleanTestFeature1"); |
||||
|
group.AddFeature("BooleanTestFeature2"); |
||||
|
group.AddFeature("IntegerTestFeature1", defaultValue: "1"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,56 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Features |
||||
|
{ |
||||
|
public class TestFeatureStore : IFeatureStore, ISingletonDependency |
||||
|
{ |
||||
|
public const string Tenant1IdValue = "f460fcf7-f944-469a-967b-3b2463323dfe"; |
||||
|
public const string Tenant2IdValue = "e10428ad-4608-4c34-a304-6f82502156f2"; |
||||
|
|
||||
|
public static Guid Tenant1Id = new Guid(Tenant1IdValue); |
||||
|
public static Guid Tenant2Id = new Guid(Tenant2IdValue); |
||||
|
|
||||
|
private readonly List<SettingRecord> _settingRecords; |
||||
|
|
||||
|
public TestFeatureStore() |
||||
|
{ |
||||
|
_settingRecords = new List<SettingRecord> |
||||
|
{ |
||||
|
new SettingRecord("BooleanTestFeature1", TenantFeatureValueProvider.ProviderName, Tenant1Id.ToString(), "true"), |
||||
|
new SettingRecord("BooleanTestFeature2", TenantFeatureValueProvider.ProviderName, Tenant1Id.ToString(), "true"), |
||||
|
new SettingRecord("IntegerTestFeature1", TenantFeatureValueProvider.ProviderName, Tenant2Id.ToString(), "34") |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public Task<string> GetOrNullAsync(string name, string providerName, string providerKey) |
||||
|
{ |
||||
|
return Task.FromResult( |
||||
|
_settingRecords.FirstOrDefault(sr => |
||||
|
sr.Name == name && |
||||
|
sr.ProviderName == providerName && |
||||
|
sr.ProviderKey == providerKey |
||||
|
)?.Value |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
private class SettingRecord |
||||
|
{ |
||||
|
public string Name { get; } |
||||
|
public string ProviderName { get; } |
||||
|
public string ProviderKey { get; } |
||||
|
public string Value { get; } |
||||
|
|
||||
|
public SettingRecord(string name, string providerName, string providerKey, string value) |
||||
|
{ |
||||
|
Name = name; |
||||
|
ProviderName = providerName; |
||||
|
ProviderKey = providerKey; |
||||
|
Value = value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,8 +1,8 @@ |
|||||
using JetBrains.Annotations; |
using JetBrains.Annotations; |
||||
|
|
||||
namespace Volo.Abp.Authorization.Permissions |
namespace Volo.Abp.PermissionManagement |
||||
{ |
{ |
||||
public class PermissionValueProviderGrantInfo |
public class PermissionValueProviderGrantInfo //TODO: Rename to PermissionGrantInfo
|
||||
{ |
{ |
||||
public static PermissionValueProviderGrantInfo NonGranted { get; } = new PermissionValueProviderGrantInfo(false); |
public static PermissionValueProviderGrantInfo NonGranted { get; } = new PermissionValueProviderGrantInfo(false); |
||||
|
|
||||
@ -0,0 +1,6 @@ |
|||||
|
module.exports = { |
||||
|
mappings: { |
||||
|
"@node_modules/flag-icon-css/css/*": "@libs/flag-icon-css/css", |
||||
|
"@node_modules/flag-icon-css/flags/1x1/*": "@libs/flag-icon-css/flags/1x1" |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue