83 changed files with 1332 additions and 294 deletions
@ -0,0 +1,20 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.EventBus; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
[Serializable] |
||||
|
[EventName("abp.data_protection.resource_changed")] |
||||
|
public class DataAccessResourceChangeEvent |
||||
|
{ |
||||
|
public DataAccessResource Resource { get; set; } |
||||
|
public DataAccessResourceChangeEvent() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public DataAccessResourceChangeEvent(DataAccessResource resource) |
||||
|
{ |
||||
|
Resource = resource; |
||||
|
} |
||||
|
} |
||||
@ -1,6 +1,10 @@ |
|||||
{ |
{ |
||||
"culture": "en", |
"culture": "en", |
||||
"texts": { |
"texts": { |
||||
"DataProtection:010001": "Data access permission not granted to protected resources!" |
"DataProtection:010001": "Data access permission not granted to protected resources!", |
||||
|
"DisplayName:LastModifierId": "Last Modifier Id", |
||||
|
"DisplayName:LastModificationTime": "Last Modification Time", |
||||
|
"DisplayName:CreatorId": "Creator Id", |
||||
|
"DisplayName:CreationTime": "Creation Time" |
||||
} |
} |
||||
} |
} |
||||
@ -1,6 +1,10 @@ |
|||||
{ |
{ |
||||
"culture": "zh-Hans", |
"culture": "zh-Hans", |
||||
"texts": { |
"texts": { |
||||
"DataProtection:010001": "未授予受保护资源的数据访问权限!" |
"DataProtection:010001": "未授予受保护资源的数据访问权限!", |
||||
|
"DisplayName:LastModifierId": "上次修改人", |
||||
|
"DisplayName:LastModificationTime": "上次修改时间", |
||||
|
"DisplayName:CreatorId": "创建人", |
||||
|
"DisplayName:CreationTime": "创建时间" |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace LINGYUN.Abp.DataProtection.Models; |
||||
|
|
||||
|
public class EntityEnumInfoModel |
||||
|
{ |
||||
|
public string Key { get; set; } |
||||
|
public object Value { get; set; } |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
namespace LINGYUN.Abp.DataProtection.Models; |
||||
|
|
||||
|
public class EntityPropertyInfoModel |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 名称
|
||||
|
/// </summary>
|
||||
|
public string Name { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 显示名称
|
||||
|
/// </summary>
|
||||
|
public string DisplayName { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 类型全名
|
||||
|
/// </summary>
|
||||
|
public string TypeFullName { get; set; } |
||||
|
/// <summary>
|
||||
|
/// JavaScript类型
|
||||
|
/// </summary>
|
||||
|
public string JavaScriptType { get; set; } |
||||
|
/// <summary>
|
||||
|
/// JavaScript名称
|
||||
|
/// </summary>
|
||||
|
public string JavaScriptName { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 枚举列表
|
||||
|
/// </summary>
|
||||
|
public EntityEnumInfoModel[] Enums { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 允许的过滤操作列表
|
||||
|
/// </summary>
|
||||
|
public DataAccessFilterOperate[] Operates { get; set; } |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtection.Models; |
||||
|
|
||||
|
public class EntityTypeInfoGetModel |
||||
|
{ |
||||
|
[Required] |
||||
|
public DataAccessOperation Operation { get; set; } |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtection.Models; |
||||
|
|
||||
|
public class EntityTypeInfoModel |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 实体名称
|
||||
|
/// </summary>
|
||||
|
public string Name { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 显示名称
|
||||
|
/// </summary>
|
||||
|
public string DisplayName { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 可访问属性列表
|
||||
|
/// </summary>
|
||||
|
public List<EntityPropertyInfoModel> Properties { get; set; } = new List<EntityPropertyInfoModel>(); |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,24 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0</TargetFrameworks> |
||||
|
<AssemblyName>LINGYUN.Abp.DataProtection.Application.Contracts</AssemblyName> |
||||
|
<PackageId>LINGYUN.Abp.DataProtection.Application.Contracts</PackageId> |
||||
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
||||
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
||||
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.DataProtection.Abstractions\LINGYUN.Abp.DataProtection.Abstractions.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,12 @@ |
|||||
|
using Volo.Abp.Application; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpDataProtectionAbstractionsModule), |
||||
|
typeof(AbpDddApplicationContractsModule))] |
||||
|
public class AbpDataProtectionApplicationContractsModule : AbpModule |
||||
|
{ |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
public class EntityEnumInfoDto |
||||
|
{ |
||||
|
public string Key { get; set; } |
||||
|
public object Value { get; set; } |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
public class EntityPropertyInfoDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 名称
|
||||
|
/// </summary>
|
||||
|
public string Name { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 显示名称
|
||||
|
/// </summary>
|
||||
|
public string DisplayName { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 类型全名
|
||||
|
/// </summary>
|
||||
|
public string TypeFullName { get; set; } |
||||
|
/// <summary>
|
||||
|
/// JavaScript类型
|
||||
|
/// </summary>
|
||||
|
public string JavaScriptType { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 枚举列表
|
||||
|
/// </summary>
|
||||
|
public EntityEnumInfoDto[] Enums { get; set; } = new EntityEnumInfoDto[0]; |
||||
|
/// <summary>
|
||||
|
/// 允许的过滤操作列表
|
||||
|
/// </summary>
|
||||
|
public DataAccessFilterOperate[] Operates { get; set; } |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
public class EntityTypeInfoDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 实体名称
|
||||
|
/// </summary>
|
||||
|
public string Name { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 显示名称
|
||||
|
/// </summary>
|
||||
|
public string DisplayName { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 可访问属性列表
|
||||
|
/// </summary>
|
||||
|
public EntityPropertyInfoDto[] Properties { get; set; } = new EntityPropertyInfoDto[0]; |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
public class EntityTypeInfoGetInput |
||||
|
{ |
||||
|
public DataAccessOperation Operation { get; set; } |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
public interface IEntityTypeInfoAppService : IApplicationService |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 获取实体可访问规则
|
||||
|
/// </summary>
|
||||
|
/// <param name="input"></param>
|
||||
|
/// <returns></returns>
|
||||
|
Task<EntityTypeInfoDto> GetEntityRuleAsync(EntityTypeInfoGetInput input); |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,25 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0</TargetFrameworks> |
||||
|
<AssemblyName>LINGYUN.Abp.DataProtection.Application</AssemblyName> |
||||
|
<PackageId>LINGYUN.Abp.DataProtection.Application</PackageId> |
||||
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
||||
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
||||
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Ddd.Application" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.DataProtection\LINGYUN.Abp.DataProtection.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.DataProtection.Application.Contracts\LINGYUN.Abp.DataProtection.Application.Contracts.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,13 @@ |
|||||
|
using Volo.Abp.Application; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpDataProtectionApplicationContractsModule), |
||||
|
typeof(AbpDataProtectionModule), |
||||
|
typeof(AbpDddApplicationModule))] |
||||
|
public class AbpDataProtectionApplicationModule : AbpModule |
||||
|
{ |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
using Microsoft.AspNetCore.Authorization; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
public abstract class EntityTypeInfoAppService<TEntity> : ApplicationService, IEntityTypeInfoAppService |
||||
|
{ |
||||
|
protected IDataAccessEntityTypeInfoProvider EntityTypeInfoProvider => LazyServiceProvider.GetRequiredService<IDataAccessEntityTypeInfoProvider>(); |
||||
|
|
||||
|
[Authorize] |
||||
|
public virtual async Task<EntityTypeInfoDto> GetEntityRuleAsync(EntityTypeInfoGetInput input) |
||||
|
{ |
||||
|
var entityType = typeof(TEntity); |
||||
|
var resourceType = LocalizationResource ?? typeof(DefaultResource); |
||||
|
|
||||
|
var context = new DataAccessEntitTypeInfoContext( |
||||
|
entityType, |
||||
|
resourceType, |
||||
|
input.Operation, |
||||
|
LazyServiceProvider); |
||||
|
|
||||
|
var model = await EntityTypeInfoProvider.GetEntitTypeInfoAsync(context); |
||||
|
|
||||
|
return new EntityTypeInfoDto |
||||
|
{ |
||||
|
Name = model.Name, |
||||
|
DisplayName = model.DisplayName, |
||||
|
Properties = model.Properties.Select(prop => new EntityPropertyInfoDto |
||||
|
{ |
||||
|
Name = prop.Name, |
||||
|
DisplayName = prop.DisplayName, |
||||
|
TypeFullName = prop.TypeFullName, |
||||
|
JavaScriptType = prop.JavaScriptType, |
||||
|
Operates = prop.Operates, |
||||
|
Enums = prop.Enums.Select(em => new EntityEnumInfoDto |
||||
|
{ |
||||
|
Key = em.Key, |
||||
|
Value = em.Value, |
||||
|
}).ToArray() |
||||
|
}).ToArray(), |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
namespace System; |
||||
|
|
||||
|
internal static class NullableTypeExtensions |
||||
|
{ |
||||
|
public static bool IsNullableType(this Type theType) => |
||||
|
theType.IsGenericType(typeof(Nullable<>)); |
||||
|
|
||||
|
public static bool IsGenericType(this Type type, Type genericType) => |
||||
|
type.IsGenericType && type.GetGenericTypeDefinition() == genericType; |
||||
|
} |
||||
@ -1,64 +0,0 @@ |
|||||
using Microsoft.EntityFrameworkCore.Diagnostics; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using System; |
|
||||
using System.Linq; |
|
||||
using System.Linq.Expressions; |
|
||||
using System.Reflection; |
|
||||
using Volo.Abp.Data; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
using Volo.Abp.Domain.Entities; |
|
||||
using Volo.Abp.Users; |
|
||||
|
|
||||
namespace LINGYUN.Abp.DataProtection.EntityFrameworkCore; |
|
||||
public class AbpDataProtectedReadEntityInterceptor : IQueryExpressionInterceptor, ITransientDependency |
|
||||
{ |
|
||||
public IAbpLazyServiceProvider LazyServiceProvider { get; set; } = default!; |
|
||||
public IOptions<AbpDataProtectionOptions> DataProtectionOptions => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpDataProtectionOptions>>(); |
|
||||
public ICurrentUser CurrentUser => LazyServiceProvider.LazyGetRequiredService<ICurrentUser>(); |
|
||||
public IDataFilter DataFilter => LazyServiceProvider.LazyGetRequiredService<IDataFilter>(); |
|
||||
public IEntityTypeFilterBuilder EntityTypeFilterBuilder => LazyServiceProvider.LazyGetRequiredService<IEntityTypeFilterBuilder>(); |
|
||||
|
|
||||
private static readonly MethodInfo WhereMethodInfo = typeof(Queryable).GetMethods().First(m => m.Name == nameof(Queryable.Where)); |
|
||||
|
|
||||
public Expression QueryCompilationStarting(Expression queryExpression, QueryExpressionEventData eventData) |
|
||||
{ |
|
||||
if (DataFilter.IsEnabled<IDataProtected>() && queryExpression.Type.GenericTypeArguments.Length > 0) |
|
||||
{ |
|
||||
var entityType = queryExpression.Type.GenericTypeArguments[0]; |
|
||||
var exp = EntityTypeFilterBuilder.Build(entityType, DataAccessOperation.Read); |
|
||||
|
|
||||
return Expression.Call( |
|
||||
method: WhereMethodInfo.MakeGenericMethod(entityType), |
|
||||
arg0: queryExpression, |
|
||||
arg1: exp); |
|
||||
} |
|
||||
|
|
||||
return queryExpression; |
|
||||
} |
|
||||
|
|
||||
public class DataProtectedExpressionVisitor : ExpressionVisitor |
|
||||
{ |
|
||||
private readonly Type _entityType; |
|
||||
private readonly IEntityTypeFilterBuilder _entityTypeFilterBuilder; |
|
||||
|
|
||||
public DataProtectedExpressionVisitor(Type entityType, IEntityTypeFilterBuilder entityTypeFilterBuilder) |
|
||||
{ |
|
||||
_entityType = entityType; |
|
||||
_entityTypeFilterBuilder = entityTypeFilterBuilder; |
|
||||
} |
|
||||
|
|
||||
private static readonly MethodInfo WhereMethodInfo = typeof(Queryable).GetMethods().First(m => m.Name == nameof(Queryable.Where)); |
|
||||
|
|
||||
protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) |
|
||||
{ |
|
||||
var method = WhereMethodInfo.MakeGenericMethod(_entityType); |
|
||||
var args0 = base.VisitMethodCall(methodCallExpression); |
|
||||
var args1 = _entityTypeFilterBuilder.Build(_entityType, DataAccessOperation.Read); |
|
||||
|
|
||||
return Expression.Call( |
|
||||
method: method, |
|
||||
arg0: args0, |
|
||||
arg1: args1); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,22 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
public class DataAccessEntitTypeInfoContext |
||||
|
{ |
||||
|
public Type EntityType { get; } |
||||
|
public Type ResourceType { get; } |
||||
|
public DataAccessOperation Operation { get; } |
||||
|
public IServiceProvider ServiceProvider { get; } |
||||
|
public DataAccessEntitTypeInfoContext( |
||||
|
Type entityType, |
||||
|
Type resourceType, |
||||
|
DataAccessOperation operation, |
||||
|
IServiceProvider serviceProvider) |
||||
|
{ |
||||
|
EntityType = entityType; |
||||
|
ResourceType = resourceType; |
||||
|
Operation = operation; |
||||
|
ServiceProvider = serviceProvider; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,103 @@ |
|||||
|
using LINGYUN.Abp.DataProtection.Models; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
public class DataAccessEntityTypeInfoProvider : IDataAccessEntityTypeInfoProvider, ISingletonDependency |
||||
|
{ |
||||
|
public async virtual Task<EntityTypeInfoModel> GetEntitTypeInfoAsync(DataAccessEntitTypeInfoContext context) |
||||
|
{ |
||||
|
var allowProperties = new List<string>(); |
||||
|
|
||||
|
var dataProtectionOptions = context.ServiceProvider.GetRequiredService<IOptions<AbpDataProtectionOptions>>().Value; |
||||
|
var javaScriptTypeConvert = context.ServiceProvider.GetRequiredService<IJavaScriptTypeConvert>(); |
||||
|
var localizerFactory = context.ServiceProvider.GetRequiredService<IStringLocalizerFactory>(); |
||||
|
var stringLozalizer = localizerFactory.Create(context.ResourceType); |
||||
|
|
||||
|
var entityTypeRuleModel = new EntityTypeInfoModel |
||||
|
{ |
||||
|
Name = context.EntityType.Name, |
||||
|
DisplayName = stringLozalizer[$"DisplayName:{context.EntityType.Name}"].Value ?? context.EntityType.Name |
||||
|
}; |
||||
|
|
||||
|
var subjectContext = new DataAccessSubjectContributorContext( |
||||
|
context.EntityType.FullName, |
||||
|
context.Operation, |
||||
|
context.ServiceProvider); |
||||
|
|
||||
|
foreach (var subjectContributor in dataProtectionOptions.SubjectContributors) |
||||
|
{ |
||||
|
var subjectAllowProperties = await subjectContributor.GetAccessdProperties(subjectContext); |
||||
|
|
||||
|
allowProperties.AddIfNotContains(subjectAllowProperties); |
||||
|
} |
||||
|
|
||||
|
IEnumerable<PropertyInfo> entityPropeties = context.EntityType.GetProperties(); |
||||
|
|
||||
|
if (allowProperties.Count > 0) |
||||
|
{ |
||||
|
if (dataProtectionOptions.EntityIgnoreProperties.TryGetValue(context.EntityType, out var entityIgnoreProps)) |
||||
|
{ |
||||
|
allowProperties.AddIfNotContains(entityIgnoreProps); |
||||
|
} |
||||
|
|
||||
|
allowProperties.AddIfNotContains(dataProtectionOptions.GlobalIgnoreProperties); |
||||
|
|
||||
|
entityPropeties = entityPropeties.Where(x => allowProperties.Contains(x.Name)); |
||||
|
} |
||||
|
|
||||
|
foreach (var propertyInfo in entityPropeties) |
||||
|
{ |
||||
|
// 字段本地化描述规则
|
||||
|
// 在本地化文件中定义 DisplayName:PropertyName
|
||||
|
var localizedProp = stringLozalizer[$"DisplayName:{propertyInfo.Name}"]; |
||||
|
var propertyInfoResult = javaScriptTypeConvert.Convert(propertyInfo.PropertyType); |
||||
|
var entityPropertyInfo = new EntityPropertyInfoModel |
||||
|
{ |
||||
|
Name = propertyInfo.Name, |
||||
|
TypeFullName = propertyInfo.PropertyType.FullName, |
||||
|
DisplayName = localizedProp.Value ?? propertyInfo.Name, |
||||
|
JavaScriptType = propertyInfoResult.Type, |
||||
|
JavaScriptName = propertyInfo.Name.ToCamelCase(), |
||||
|
Operates = propertyInfoResult.AllowOperates |
||||
|
}; |
||||
|
|
||||
|
var propertyType = propertyInfo.PropertyType; |
||||
|
if (propertyType.IsNullableType()) |
||||
|
{ |
||||
|
propertyType = propertyType.GetGenericArguments().FirstOrDefault(); |
||||
|
} |
||||
|
|
||||
|
if (typeof(Enum).IsAssignableFrom(propertyType)) |
||||
|
{ |
||||
|
var enumNames = Enum.GetNames(propertyType); |
||||
|
var enumValues = Enum.GetValues(propertyType); |
||||
|
var paramterOptions = new EntityEnumInfoModel[enumNames.Length]; |
||||
|
for (var index = 0; index < enumNames.Length; index++) |
||||
|
{ |
||||
|
var enumName = enumNames[index]; |
||||
|
var localizerEnumKey = $"{propertyInfo.Name}:{enumName}"; |
||||
|
var localizerEnumName = stringLozalizer[localizerEnumKey]; |
||||
|
paramterOptions[index] = new EntityEnumInfoModel |
||||
|
{ |
||||
|
Key = localizerEnumName.ResourceNotFound ? enumName : localizerEnumName.Value, |
||||
|
Value = enumValues.GetValue(index), |
||||
|
}; |
||||
|
} |
||||
|
entityPropertyInfo.Enums = paramterOptions; |
||||
|
} |
||||
|
|
||||
|
entityTypeRuleModel.Properties.Add(entityPropertyInfo); |
||||
|
} |
||||
|
|
||||
|
return entityTypeRuleModel; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.EventBus.Distributed; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
public class DataAccessResourceCacheInvalidator : IDistributedEventHandler<DataAccessResourceChangeEvent>, ITransientDependency |
||||
|
{ |
||||
|
private readonly IDataProtectedResourceStore _resourceStore; |
||||
|
public DataAccessResourceCacheInvalidator(IDataProtectedResourceStore resourceStore) |
||||
|
{ |
||||
|
_resourceStore = resourceStore; |
||||
|
} |
||||
|
public async virtual Task HandleEventAsync(DataAccessResourceChangeEvent eventData) |
||||
|
{ |
||||
|
await _resourceStore.SetAsync(eventData.Resource); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using LINGYUN.Abp.DataProtection.Models; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
public interface IDataAccessEntityTypeInfoProvider |
||||
|
{ |
||||
|
Task<EntityTypeInfoModel> GetEntitTypeInfoAsync(DataAccessEntitTypeInfoContext context); |
||||
|
} |
||||
@ -1,9 +1,10 @@ |
|||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
namespace LINGYUN.Abp.DataProtection; |
namespace LINGYUN.Abp.DataProtection; |
||||
public interface IDataAccessSubjectContributor |
public interface IDataAccessSubjectContributor |
||||
{ |
{ |
||||
string Name { get; } |
string Name { get; } |
||||
List<DataAccessFilterGroup> GetFilterGroups(DataAccessSubjectContributorContext context); |
Task<List<DataAccessFilterGroup>> GetFilterGroups(DataAccessSubjectContributorContext context); |
||||
List<string> GetAllowProperties(DataAccessSubjectContributorContext context); |
Task<List<string>> GetAccessdProperties(DataAccessSubjectContributorContext context); |
||||
} |
} |
||||
|
|||||
@ -1,9 +1,11 @@ |
|||||
namespace LINGYUN.Abp.DataProtection; |
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
public interface IDataProtectedResourceStore |
public interface IDataProtectedResourceStore |
||||
{ |
{ |
||||
void Set(DataAccessResource resource); |
Task SetAsync(DataAccessResource resource); |
||||
|
|
||||
void Remove(DataAccessResource resource); |
Task RemoveAsync(DataAccessResource resource); |
||||
|
|
||||
DataAccessResource Get(string subjectName, string subjectId, string entityTypeFullName, DataAccessOperation operation); |
Task<DataAccessResource> GetAsync(string subjectName, string subjectId, string entityTypeFullName, DataAccessOperation operation); |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,8 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
public interface IJavaScriptTypeConvert |
||||
|
{ |
||||
|
JavaScriptTypeConvertResult Convert(Type propertyType); |
||||
|
} |
||||
@ -0,0 +1,132 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
public class JavaScriptTypeConvert : IJavaScriptTypeConvert, ISingletonDependency |
||||
|
{ |
||||
|
public JavaScriptTypeConvertResult Convert(Type propertyType) |
||||
|
{ |
||||
|
var (JavaScriptType, AccessOperates) = InnerConvert(propertyType); |
||||
|
|
||||
|
return new JavaScriptTypeConvertResult(JavaScriptType, AccessOperates); |
||||
|
} |
||||
|
|
||||
|
protected virtual (string JavaScriptType, DataAccessFilterOperate[] AccessFilterOperates) InnerConvert(Type propertyType) |
||||
|
{ |
||||
|
var availableComparator = new List<DataAccessFilterOperate>(); |
||||
|
if (propertyType.IsNullableType()) |
||||
|
{ |
||||
|
propertyType = propertyType.GetGenericArguments().FirstOrDefault(); |
||||
|
} |
||||
|
|
||||
|
if (typeof(Enum).IsAssignableFrom(propertyType)) |
||||
|
{ |
||||
|
// 枚举类型只支持如下操作符
|
||||
|
// 小于、小于等于、大于、大于等于、等于、不等于、空、非空
|
||||
|
availableComparator.AddRange(new[] |
||||
|
{ |
||||
|
DataAccessFilterOperate.Greater, |
||||
|
DataAccessFilterOperate.GreaterOrEqual, |
||||
|
DataAccessFilterOperate.Less, |
||||
|
DataAccessFilterOperate.LessOrEqual, |
||||
|
DataAccessFilterOperate.Equal, |
||||
|
DataAccessFilterOperate.NotEqual, |
||||
|
}); |
||||
|
return ("number", availableComparator.ToArray()); |
||||
|
} |
||||
|
|
||||
|
var typeFullName = propertyType.FullName; |
||||
|
|
||||
|
switch (typeFullName) |
||||
|
{ |
||||
|
case "System.Int16": |
||||
|
case "System.Int32": |
||||
|
case "System.Int64": |
||||
|
case "System.UInt16": |
||||
|
case "System.UInt32": |
||||
|
case "System.UInt64": |
||||
|
case "System.Single": |
||||
|
case "System.Double": |
||||
|
case "System.Byte": |
||||
|
case "System.SByte": |
||||
|
case "System.Decimal": |
||||
|
// 数值类型只支持如下操作符
|
||||
|
// 小于、小于等于、大于、大于等于、等于、不等于、空、非空
|
||||
|
availableComparator.AddRange(new[] |
||||
|
{ |
||||
|
DataAccessFilterOperate.Greater, |
||||
|
DataAccessFilterOperate.GreaterOrEqual, |
||||
|
DataAccessFilterOperate.Less, |
||||
|
DataAccessFilterOperate.LessOrEqual, |
||||
|
DataAccessFilterOperate.Equal, |
||||
|
DataAccessFilterOperate.NotEqual, |
||||
|
}); |
||||
|
return ("number", availableComparator.ToArray()); |
||||
|
case "System.Boolean": |
||||
|
// 布尔类型只支持如下操作符
|
||||
|
// 等于、不等于、空、非空
|
||||
|
availableComparator.AddRange(new[] |
||||
|
{ |
||||
|
DataAccessFilterOperate.Equal, |
||||
|
DataAccessFilterOperate.NotEqual, |
||||
|
}); |
||||
|
return ("boolean", availableComparator.ToArray()); |
||||
|
case "System.Guid": |
||||
|
// Guid类型只支持如下操作符
|
||||
|
// 等于、不等于、空、非空
|
||||
|
availableComparator.AddRange(new[] |
||||
|
{ |
||||
|
DataAccessFilterOperate.Equal, |
||||
|
DataAccessFilterOperate.NotEqual, |
||||
|
}); |
||||
|
return ("string", availableComparator.ToArray()); |
||||
|
case "System.Char": |
||||
|
case "System.String": |
||||
|
// 字符类型支持所有操作符
|
||||
|
return ("string", availableComparator.ToArray()); |
||||
|
case "System.DateTime": |
||||
|
// 时间类型只支持如下操作符
|
||||
|
// 小于、小于等于、大于、大于等于、等于、不等于、空、非空
|
||||
|
availableComparator.AddRange(new[] |
||||
|
{ |
||||
|
DataAccessFilterOperate.Greater, |
||||
|
DataAccessFilterOperate.GreaterOrEqual, |
||||
|
DataAccessFilterOperate.Less, |
||||
|
DataAccessFilterOperate.LessOrEqual, |
||||
|
DataAccessFilterOperate.Equal, |
||||
|
DataAccessFilterOperate.NotEqual, |
||||
|
}); |
||||
|
return ("Date", availableComparator.ToArray()); |
||||
|
default: |
||||
|
case "System.Object": |
||||
|
case "System.DBNull": |
||||
|
if (propertyType.IsArray) |
||||
|
{ |
||||
|
// 数组类型只支持如下操作符
|
||||
|
// 包含、不包含、空、非空
|
||||
|
availableComparator.AddRange(new[] |
||||
|
{ |
||||
|
DataAccessFilterOperate.Contains, |
||||
|
DataAccessFilterOperate.NotContains, |
||||
|
}); |
||||
|
|
||||
|
return ("array", availableComparator.ToArray()); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// 未知对象类型只支持如下操作符
|
||||
|
// 等于、不等于、空、非空
|
||||
|
availableComparator.AddRange(new[] |
||||
|
{ |
||||
|
DataAccessFilterOperate.Equal, |
||||
|
DataAccessFilterOperate.NotEqual, |
||||
|
}); |
||||
|
return ("object", availableComparator.ToArray()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
namespace LINGYUN.Abp.DataProtection; |
||||
|
|
||||
|
public class JavaScriptTypeConvertResult |
||||
|
{ |
||||
|
public string Type { get; } |
||||
|
public DataAccessFilterOperate[] AllowOperates { get; } |
||||
|
public JavaScriptTypeConvertResult(string type, DataAccessFilterOperate[] allowOperates) |
||||
|
{ |
||||
|
Type = type; |
||||
|
AllowOperates = allowOperates; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Application.Dtos; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtectionManagement; |
||||
|
|
||||
|
public class EntityEnumInfoDto : EntityDto<Guid> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 名称
|
||||
|
/// </summary>
|
||||
|
public string Name { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 显示名称
|
||||
|
/// </summary>
|
||||
|
public string DisplayName { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 枚举值
|
||||
|
/// </summary>
|
||||
|
public string Value { get; set; } |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
namespace LINGYUN.Abp.DataProtectionManagement; |
||||
|
|
||||
|
public static class EntityEnumInfoConsts |
||||
|
{ |
||||
|
public static int MaxNameLength { get; set; } = EntityTypeInfoConsts.MaxNameLength; |
||||
|
public static int MaxDisplayNameLength { get; set; } = EntityTypeInfoConsts.MaxDisplayNameLength; |
||||
|
public static int MaxValueLength { get; set; } = 10; |
||||
|
} |
||||
@ -1,56 +0,0 @@ |
|||||
using LINGYUN.Abp.Authorization.Permissions; |
|
||||
using LINGYUN.Abp.DataProtection; |
|
||||
using System.Linq; |
|
||||
using System.Threading.Tasks; |
|
||||
using Volo.Abp.Authorization.Permissions; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
using Volo.Abp.Domain.Entities.Events; |
|
||||
using Volo.Abp.EventBus; |
|
||||
|
|
||||
namespace LINGYUN.Abp.DataProtectionManagement; |
|
||||
public class DataProtectedResourceCacheItemInvalidator : |
|
||||
ILocalEventHandler<EntityChangedEventData<RoleEntityRule>>, |
|
||||
ILocalEventHandler<EntityChangedEventData<OrganizationUnitEntityRule>>, |
|
||||
ITransientDependency |
|
||||
{ |
|
||||
private readonly IDataProtectedResourceCache _resourceCache; |
|
||||
|
|
||||
public DataProtectedResourceCacheItemInvalidator(IDataProtectedResourceCache resourceCache) |
|
||||
{ |
|
||||
_resourceCache = resourceCache; |
|
||||
} |
|
||||
|
|
||||
public virtual Task HandleEventAsync(EntityChangedEventData<RoleEntityRule> eventData) |
|
||||
{ |
|
||||
var dataResource = new DataAccessResource( |
|
||||
RolePermissionValueProvider.ProviderName, |
|
||||
eventData.Entity.RoleName, |
|
||||
eventData.Entity.EntityTypeFullName, |
|
||||
eventData.Entity.Operation, |
|
||||
eventData.Entity.FilterGroup) |
|
||||
{ |
|
||||
AllowProperties = eventData.Entity.AllowProperties?.Split(",").ToList(), |
|
||||
}; |
|
||||
|
|
||||
_resourceCache.SetCache(dataResource); |
|
||||
|
|
||||
return Task.CompletedTask; |
|
||||
} |
|
||||
|
|
||||
public virtual Task HandleEventAsync(EntityChangedEventData<OrganizationUnitEntityRule> eventData) |
|
||||
{ |
|
||||
var dataResource = new DataAccessResource( |
|
||||
OrganizationUnitPermissionValueProvider.ProviderName, |
|
||||
eventData.Entity.OrgCode, |
|
||||
eventData.Entity.EntityTypeFullName, |
|
||||
eventData.Entity.Operation, |
|
||||
eventData.Entity.FilterGroup) |
|
||||
{ |
|
||||
AllowProperties = eventData.Entity.AllowProperties?.Split(",").ToList(), |
|
||||
}; |
|
||||
|
|
||||
_resourceCache.SetCache(dataResource); |
|
||||
|
|
||||
return Task.CompletedTask; |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,47 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Domain.Entities; |
||||
|
|
||||
|
namespace LINGYUN.Abp.DataProtectionManagement; |
||||
|
|
||||
|
public class EntityEnumInfo : Entity<Guid> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 名称
|
||||
|
/// </summary>
|
||||
|
public virtual string Name { get; protected set; } |
||||
|
/// <summary>
|
||||
|
/// 显示名称
|
||||
|
/// </summary>
|
||||
|
public virtual string DisplayName { get; protected set; } |
||||
|
/// <summary>
|
||||
|
/// 枚举值
|
||||
|
/// </summary>
|
||||
|
public virtual string Value { get; protected set; } |
||||
|
/// <summary>
|
||||
|
/// 所属属性
|
||||
|
/// </summary>
|
||||
|
public virtual EntityPropertyInfo PropertyInfo { get; protected set; } |
||||
|
/// <summary>
|
||||
|
/// 所属属性标识
|
||||
|
/// </summary>
|
||||
|
public virtual Guid PropertyInfoId { get; protected set; } |
||||
|
protected EntityEnumInfo() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public EntityEnumInfo( |
||||
|
Guid id, |
||||
|
Guid propertyInfoId, |
||||
|
string name, |
||||
|
string displayName, |
||||
|
string value) |
||||
|
: base(id) |
||||
|
{ |
||||
|
PropertyInfoId = propertyInfoId; |
||||
|
Name = Check.NotNullOrWhiteSpace(name, nameof(name), EntityEnumInfoConsts.MaxNameLength); |
||||
|
DisplayName = Check.NotNullOrWhiteSpace(displayName, nameof(displayName), EntityEnumInfoConsts.MaxDisplayNameLength); |
||||
|
Value = Check.NotNullOrWhiteSpace(value, nameof(value), EntityEnumInfoConsts.MaxValueLength); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue