Browse Source

Merge pull request #926 from colinin/refactor-data-protected

重新定义数据权限结构
pull/930/head
yx lin 2 years ago
committed by GitHub
parent
commit
68c9120f3b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 7
      aspnet-core/LINGYUN.MicroService.All.sln
  2. 137
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/AbpDataProtectionDbContext.cs
  3. 80
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/DataProtectionQueryExpressionInterceptor.cs
  4. 297
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/EfCoreDataProtectionRepositoryBase.cs
  5. 4
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/ProtectedEntityHelper.cs
  6. 6
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN.Abp.DataProtection.csproj
  7. 15
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/AbpDataProtectionModule.cs
  8. 30
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessCacheItem.cs
  9. 12
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessEntityRule.cs
  10. 11
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessExpressionRule.cs
  11. 19
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessFiledRule.cs
  12. 7
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessOperation.cs
  13. 34
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessOwner.cs
  14. 23
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessRole.cs
  15. 54
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessRule.cs
  16. 11
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessRuleInfo.cs
  17. 352
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataProtectionAsyncQueryableProvider.cs
  18. 12
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/ExpressionType.cs
  19. 8
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/IDataAccessCache.cs
  20. 19
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/IDataProtectdChecker.cs
  21. 11
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/IDataProtection.cs
  22. 5
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/IHasDataAccess.cs
  23. 29
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/ProtectBehavior.cs
  24. 22
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/ProtectedField.cs
  25. 40
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/ProtectedFieldRule.cs
  26. 27
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/ProtectedResource.cs
  27. 19
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/ResourceGrantedResult.cs
  28. 24
      aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/Volo/Abp/Uow/IUnitOfWorkDataAccessExtensions.cs
  29. 2
      aspnet-core/tests/LINGYUN.Abp.DataProtection.Tests/LINGYUN/Abp/DataProtection/AbpDataProtectionTestModule.cs
  30. 5
      aspnet-core/tests/LINGYUN.Abp.DataProtection.Tests/LINGYUN/Abp/DataProtection/EfCoreFakeProtectionObjectRepository.cs
  31. 25
      aspnet-core/tests/LINGYUN.Abp.DataProtection.Tests/LINGYUN/Abp/DataProtection/FakeDataProtectdChecker.cs
  32. 24
      aspnet-core/tests/LINGYUN.Abp.DataProtection.Tests/LINGYUN/Abp/DataProtection/FakeDataProtectedDbContext.cs
  33. 4
      aspnet-core/tests/LINGYUN.Abp.DataProtection.Tests/LINGYUN/Abp/DataProtection/FakeProtectionObject.cs
  34. 309
      aspnet-core/tests/LINGYUN.Abp.DataProtection.Tests/LINGYUN/Abp/DataProtection/ProtectionFieldTests.cs

7
aspnet-core/LINGYUN.MicroService.All.sln

@ -702,6 +702,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Dynamic.Queryab
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Dynamic.Queryable.HttpApi", "framework\dynamic-queryable\LINGYUN.Abp.Dynamic.Queryable.HttpApi\LINGYUN.Abp.Dynamic.Queryable.HttpApi.csproj", "{014A9583-0EAA-48A4-ACBE-07DC88159E13}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.DataProtection.Tests", "tests\LINGYUN.Abp.DataProtection.Tests\LINGYUN.Abp.DataProtection.Tests.csproj", "{AAC0C407-B4B9-4E90-99FC-2D793AC229D9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1784,6 +1786,10 @@ Global
{014A9583-0EAA-48A4-ACBE-07DC88159E13}.Debug|Any CPU.Build.0 = Debug|Any CPU
{014A9583-0EAA-48A4-ACBE-07DC88159E13}.Release|Any CPU.ActiveCfg = Release|Any CPU
{014A9583-0EAA-48A4-ACBE-07DC88159E13}.Release|Any CPU.Build.0 = Release|Any CPU
{AAC0C407-B4B9-4E90-99FC-2D793AC229D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AAC0C407-B4B9-4E90-99FC-2D793AC229D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AAC0C407-B4B9-4E90-99FC-2D793AC229D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AAC0C407-B4B9-4E90-99FC-2D793AC229D9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -2123,6 +2129,7 @@ Global
{B6D4AADE-3ABA-45E6-9916-2F8798412549} = {4FAE314C-36CB-4E3F-85B7-41D0A428B37D}
{86E85013-7C71-4770-9323-18897A64F5B2} = {4FAE314C-36CB-4E3F-85B7-41D0A428B37D}
{014A9583-0EAA-48A4-ACBE-07DC88159E13} = {4FAE314C-36CB-4E3F-85B7-41D0A428B37D}
{AAC0C407-B4B9-4E90-99FC-2D793AC229D9} = {370D7CD5-1E17-4F3D-BBFA-03429F6D4F2F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718}

137
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/AbpDataProtectionDbContext.cs

@ -1,14 +1,145 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Volo.Abp.Domain.Entities;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
using Volo.Abp.Users;
namespace LINGYUN.Abp.DataProtection.EntityFrameworkCore
{
public class AbpDataProtectionDbContext<TDbContext> : AbpDbContext<TDbContext>
where TDbContext : DbContext
public class AbpDataProtectionDbContext : AbpDbContext<AbpDataProtectionDbContext>
{
protected ICurrentUser CurrentUser => LazyServiceProvider.LazyGetService<ICurrentUser>();
protected virtual bool IsDataAccessFilterEnabled => DataFilter?.IsEnabled<IHasDataAccess>() ?? false;
public AbpDataProtectionDbContext(
DbContextOptions<TDbContext> options) : base(options)
DbContextOptions<AbpDataProtectionDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
if (typeof(IHasDataAccess).IsAssignableFrom(entityType.ClrType))
{
modelBuilder.Entity(entityType.ClrType)
.OwnsOne(entityType.ClrType.FullName, nameof(IHasDataAccess.Owner), ownedNavigationBuilder =>
{
ownedNavigationBuilder.ToJson();
});
}
}
}
protected override void ApplyAbpConceptsForAddedEntity(EntityEntry entry)
{
base.ApplyAbpConceptsForAddedEntity(entry);
if (CurrentUser.IsAuthenticated)
{
if (entry is IHasDataAccess entity)
{
ProtectedEntityHelper.TrySetOwner(
entity,
() => new DataAccessOwner(
CurrentUser.Id,
CurrentUser.Roles,
CurrentUser.FindOrganizationUnits().Select(ou => ou.ToString()).ToArray()));
}
}
}
protected virtual DataAccessRuleInfo AccessRuleInfo => UnitOfWorkManager.Current.GetAccessRuleInfo();
protected override void HandlePropertiesBeforeSave()
{
foreach (var item in ChangeTracker.Entries().ToList())
{
HandleExtraPropertiesOnSave(item);
HandleCheckPropertiesOnSave(item);
if (item.State.IsIn(EntityState.Modified, EntityState.Deleted))
{
UpdateConcurrencyStamp(item);
}
}
}
protected virtual void HandleCheckPropertiesOnSave(EntityEntry entry)
{
// 仅当启用过滤器时检查
if (IsDataAccessFilterEnabled)
{
var entityAccessRules = AccessRuleInfo?.Rules.Where(r => r.EntityTypeFullName == entry.Metadata.ClrType.FullName);
if (entityAccessRules != null)
{
if (entry.State.IsIn(EntityState.Modified, EntityState.Added, EntityState.Deleted))
{
var entityAccessRule = entityAccessRules.FirstOrDefault(r => r.Operation.IsIn(DataAccessOperation.Write, DataAccessOperation.Delete));
if (entityAccessRule != null)
{
if (entityAccessRule.Fileds.Count != 0)
{
var notAccessProps = entry.Properties.Where(p => !entityAccessRule.Fileds.Any(f => f.Field == p.Metadata.Name));
if (notAccessProps != null)
{
foreach (var property in notAccessProps)
{
// 无字段权限不做变更
property.CurrentValue = property.OriginalValue;
}
}
}
}
else
{
// 无实体变更权限不做修改
entry.State = EntityState.Unchanged;
}
}
}
}
}
protected override Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>()
{
var expression = base.CreateFilterExpression<TEntity>();
if (typeof(IHasDataAccess).IsAssignableFrom(typeof(TEntity)))
{
Expression<Func<TEntity, bool>> expression2 = (TEntity e) => !IsDataAccessFilterEnabled || CreateFilterExpression(e, AccessRuleInfo);
expression = (Expression<Func<TEntity, bool>>)((expression == null) ? ((LambdaExpression)expression2) : ((LambdaExpression)QueryFilterExpressionHelper.CombineExpressions(expression, expression2)));
}
return expression;
}
protected static bool CreateFilterExpression<TEntity>(TEntity entity, DataAccessRuleInfo accessRuleInfo)
{
if (accessRuleInfo == null)
{
return true;
}
if (!accessRuleInfo.Rules.Any(r => r.EntityTypeFullName == typeof(TEntity).FullName))
{
return false;
}
if (entity is not IHasDataAccess accessEntity)
{
return true;
}
// TODO: 需要完成详细的过滤条件
return false;
}
}
}

80
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/DataProtectionQueryExpressionInterceptor.cs

@ -1,47 +1,85 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Users;
namespace LINGYUN.Abp.DataProtection.EntityFrameworkCore;
public class DataProtectionQueryExpressionInterceptor : IQueryExpressionInterceptor
{
public Expression QueryCompilationStarting(Expression queryExpression, QueryExpressionEventData eventData)
{
return new DataProtectionExpressionVisitor().Visit(queryExpression);
var dataFilter = eventData.Context.GetService<IDataFilter>();
var dbContextProvider = eventData.Context.GetService<IDbContextProvider<AbpDataProtectionDbContext>>();
return new DataProtectionExpressionVisitor(dataFilter, dbContextProvider).Visit(queryExpression);
}
public class DataProtectionExpressionVisitor : ExpressionVisitor
{
private readonly static MethodInfo WhereMethodInfo = typeof(Queryable).GetMethod(nameof(Queryable.Where));
private readonly IDataFilter _dataFilter;
private readonly ICurrentUser _currentUser;
private readonly IDbContextProvider<AbpDataProtectionDbContext> _dbContextProvider;
public DataProtectionExpressionVisitor(
IDataFilter dataFilter,
IDbContextProvider<AbpDataProtectionDbContext> dbContextProvider)
{
_dataFilter = dataFilter;
_dbContextProvider = dbContextProvider;
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
//if (_dataFilter.IsEnabled<IDataProtection>())
//{
// var methodInfo = node!.Method;
// if (methodInfo.DeclaringType == typeof(Queryable)
// && methodInfo.Name == nameof(Queryable.Select)
// && methodInfo.GetParameters().Length == 2)
// {
// var sourceType = node.Type.GetGenericArguments()[0];
// var lambdaExpression = (LambdaExpression)((UnaryExpression)node.Arguments[1]).Operand;
// var entityParameterExpression = lambdaExpression.Parameters[0];
// var test = Expression.Call(
// method: WhereMethodInfo.MakeGenericMethod(sourceType, typeof(bool)),
// arg0: base.VisitMethodCall(node),
// arg1: Expression.Lambda(typeof(Func<,>).MakeGenericType(entityParameterExpression.Type, typeof(bool)),
// Expression.Property(entityParameterExpression, nameof(IDataProtection.Owner)),
// true));
// return test;
// }
//}
if (_dataFilter.IsEnabled<IHasDataAccess>())
{
var methodInfo = node!.Method;
if (methodInfo.DeclaringType == typeof(Queryable)
&& methodInfo.Name == nameof(Queryable.Select)
&& methodInfo.GetParameters().Length == 2)
{
var sourceType = node.Type.GetGenericArguments()[0];
var lambdaExpression = (LambdaExpression)((UnaryExpression)node.Arguments[1]).Operand;
var entityParameterExpression = lambdaExpression.Parameters[0];
var rules = _currentUser.Roles;
var ous = _currentUser.FindOrganizationUnits();
var ownerParamter = Expression.PropertyOrField(entityParameterExpression, nameof(IHasDataAccess.Owner));
if (typeof(IEntity).IsAssignableFrom(sourceType))
{
// Join params[0]
// node
var test = Expression.Call(
method: WhereMethodInfo.MakeGenericMethod(sourceType, typeof(bool)),
arg0: base.VisitMethodCall(node),
arg1: Expression.Lambda(
typeof(Func<,>).MakeGenericType(entityParameterExpression.Type, typeof(bool)),
Expression.Property(entityParameterExpression, nameof(IHasDataAccess.Owner)),
true));
return test;
}
}
}
return base.VisitMethodCall(node);
}
}

297
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/EfCoreDataProtectionRepositoryBase.cs

@ -1,297 +0,0 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Users;
namespace LINGYUN.Abp.DataProtection.EntityFrameworkCore
{
/// <summary>
/// 受保护的资源仓储接口需要继承此接口,否则需要自行实现过滤器
/// </summary>
/// <typeparam name="TDbContext"></typeparam>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TKey"></typeparam>
public abstract class EfCoreDataProtectionRepositoryBase<TDbContext, TEntity, TKey> : EfCoreRepository<TDbContext, TEntity, TKey>
where TEntity : class, IEntity<TKey>, IDataProtection
where TDbContext: IEfCoreDbContext
{
protected ICurrentUser CurrentUser => LazyServiceProvider.LazyGetService<ICurrentUser>();
protected IDataProtectdChecker DataProtectdChecker => LazyServiceProvider.LazyGetService<IDataProtectdChecker>();
protected EfCoreDataProtectionRepositoryBase(
IDbContextProvider<TDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public override async Task<TEntity> InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
if (CurrentUser.IsAuthenticated &&
entity is IDataProtection protectedEntity)
{
ProtectedEntityHelper.TrySetOwner(
protectedEntity,
() => string.Join(",", CurrentUser.UserName, CurrentUser.Roles.JoinAsString(",")));
}
return await base.InsertAsync(entity, autoSave, cancellationToken);
}
public override async Task InsertManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
{
if (CurrentUser.IsAuthenticated &&
typeof(IDataProtection).IsAssignableFrom(typeof(TEntity)))
{
foreach (var entity in entities)
{
ProtectedEntityHelper.TrySetOwner(
entity,
() => string.Join(",", CurrentUser.UserName, CurrentUser.Roles.JoinAsString(",")));
}
}
await base.InsertManyAsync(entities, autoSave, cancellationToken);
}
public override async Task<List<TEntity>> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = default)
{
return includeDetails
? await(await WithDetailsAsync(ProtectBehavior.Query)).ToListAsync(GetCancellationToken(cancellationToken))
: await(await GetQueryableAsync(ProtectBehavior.Query)).ToListAsync(GetCancellationToken(cancellationToken));
}
public override async Task<TEntity> FindAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
{
return includeDetails
? await(await WithDetailsAsync(ProtectBehavior.Query)).OrderBy(e => e.Id).FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken))
: await(await GetQueryableAsync(ProtectBehavior.Query)).OrderBy(e => e.Id).FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken));
}
public override async Task<List<TEntity>> GetListAsync(
Expression<Func<TEntity, bool>> predicate,
bool includeDetails = false,
CancellationToken cancellationToken = default)
{
return includeDetails
? await (await WithDetailsAsync(ProtectBehavior.Query)).Where(predicate).ToListAsync(GetCancellationToken(cancellationToken))
: await (await GetQueryableAsync(ProtectBehavior.Query)).Where(predicate).ToListAsync(GetCancellationToken(cancellationToken));
}
public override async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default)
{
var queryable = await GetQueryableAsync(ProtectBehavior.Delete);
var entity = await queryable
.FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken));
if (entity == null)
{
return;
}
await DeleteAsync(entity, autoSave, cancellationToken);
}
public override async Task DeleteManyAsync(IEnumerable<TKey> ids, bool autoSave = false, CancellationToken cancellationToken = default)
{
var queryable = await GetQueryableAsync(ProtectBehavior.Delete);
var entities = await queryable.Where(x => ids.Contains(x.Id)).ToListAsync(cancellationToken);
await DeleteManyAsync(entities, autoSave, cancellationToken);
}
public override async Task DeleteAsync(
Expression<Func<TEntity, bool>> predicate,
bool autoSave = false,
CancellationToken cancellationToken = default)
{
var queryable = await GetQueryableAsync(ProtectBehavior.Delete);
var entities = await queryable.Where(predicate).ToListAsync(cancellationToken);
await DeleteManyAsync(entities, autoSave, cancellationToken);
}
protected async virtual Task<IQueryable<TEntity>> WithDetailsAsync(ProtectBehavior behavior = ProtectBehavior.All)
{
if (typeof(IDataProtection).IsAssignableFrom(typeof(TEntity)))
{
var result = await DataProtectdChecker.IsGrantedAsync<TEntity>(behavior);
if (!result.Succeeded)
{
var queryable = await base.GetQueryableAsync();
return queryable.Where((t) => false);
}
return await WithDetailsAsync(result);
}
return await base.WithDetailsAsync();
}
protected async virtual Task<IQueryable<TEntity>> WithDetailsAsync(ResourceGrantedResult resourceGranted)
{
if (AbpEntityOptions.DefaultWithDetailsFunc == null)
{
return await GetQueryableAsync(resourceGranted);
}
return AbpEntityOptions.DefaultWithDetailsFunc(await GetQueryableAsync(resourceGranted));
}
protected async virtual Task<IQueryable<TEntity>> GetQueryableAsync(ProtectBehavior behavior = ProtectBehavior.All)
{
if (typeof(IDataProtection).IsAssignableFrom(typeof(TEntity)))
{
var result = await DataProtectdChecker.IsGrantedAsync<TEntity>(behavior);
if (!result.Succeeded)
{
var queryable = await base.GetQueryableAsync();
return queryable.Where((t) => false);
}
return await GetQueryableAsync(result);
}
return await base.GetQueryableAsync();
}
protected async virtual Task<IQueryable<TEntity>> GetQueryableAsync(ResourceGrantedResult resourceGranted)
{
var queryable = await base.GetQueryableAsync();
if (!resourceGranted.Succeeded)
{
// 无资源访问权限, 不返回结果
return queryable.Where((t) => false);
}
// 资源过滤,用户是否有对某个资源的访问权限
// 方案1、Resource.Owner In ("user1", "user2", "role1", "role2", "organization1", "...") 独立模块,业务增加Owner字段
// 方案2、Select R.* From Resource R Inner Join Protect T On T.Visitor = R.Owner Where T.Resource = 'Resource' 业务侵入,增加Protect表
queryable = FilterResource(queryable, resourceGranted.Resource);
// 对于可访问资源的进一步动态规则过滤 1 == 1 And Resource.Field1 = 'allow' And Resource.Field2 >= 100 And Resource.Field2 <= 200
queryable = FilterFieldRule(queryable, resourceGranted.Rules);
// 对于资源可访问字段过滤 Select Resource.Field1, Resource.Field2, Resource.Field3
queryable = FilterFieldReturn(queryable, resourceGranted.Fields);
return queryable;
}
protected virtual IQueryable<T> FilterResource<T>(IQueryable<T> queryable, ProtectedResource resource)
where T : IDataProtection
{
ParameterExpression pe = Expression.Parameter(typeof(T));
// 检查资源的可访问者
// any: 内置常量,允许访问所有资源
if (!resource.Visitor.IsNullOrWhiteSpace() || !resource.Visitor.Contains("any"))
{
// 过滤允许的资源访问者
// 方案一:模块之间独立,传递当前访问者即可
// Select * From Resource As R Where R.Owner LIKE ('visitor1', 'visitorRole1')
var ownerExp = Expression.PropertyOrField(pe, nameof(IDataProtection.Owner));
var visities = resource.Visitor.Split(',');
Expression visitorExpr = null;
foreach (var visitor in visities)
{
visitorExpr = visitorExpr == null
? Expression.Call(
ownerExp,
typeof(string).GetMethod(nameof(string.Contains), new Type[] { typeof(string) }),
Expression.Constant(visitor, ownerExp.Type))
: Expression.Or(
visitorExpr,
Expression.Call(
ownerExp,
typeof(string).GetMethod(nameof(string.Contains), new Type[] { typeof(string) }),
Expression.Constant(visitor, ownerExp.Type)));
}
// 方案二:节省网络带宽,快速查询
// Select R.* From Resource As R
// Inner Join Protect As P On P.Resource = 'Resource'
// Where 1 == 1
// And P.Behavior = ProtectBehavior.Query
// And ((P.Visitor = 'visitor1') Or (P.Visitor = 'visitorRole1') Or (P.Visitor = 'visitorRole2'))
queryable = queryable.Where(Expression.Lambda<Func<T, bool>>(visitorExpr, pe));
}
return queryable;
}
protected virtual IQueryable<T> FilterFieldRule<T>(IQueryable<T> queryable, IEnumerable<ProtectedFieldRule> rules)
where T : IDataProtection
{
ParameterExpression pe = Expression.Parameter(typeof(T));
// 默认未指定访问规则
// 则可访问所有允许的资源
if (rules.Any())
{
Expression<Func<T, bool>> where = PredicateBuilder.New<T>((t) => true);
foreach (var fieldRule in rules)
{
var memberExp = Expression.PropertyOrField(pe, fieldRule.Field);
Expression memberCondition = null;
memberCondition = fieldRule.Operator switch
{
// LIKE
ExpressionType.Contains => Expression.Call(
memberExp,
typeof(string).GetMethod(nameof(string.Contains), new Type[] { typeof(string) }),
Expression.Constant(fieldRule.Value, memberExp.Type)),
// ==
ExpressionType.Equal => Expression.Equal(memberExp, Expression.Constant(fieldRule.Value, memberExp.Type)),
// <
ExpressionType.LessThan => Expression.LessThan(memberExp, Expression.Constant(fieldRule.Value, memberExp.Type)),
// <=
ExpressionType.LessThanOrEqual => Expression.LessThanOrEqual(memberExp, Expression.Constant(fieldRule.Value, memberExp.Type)),
// >
ExpressionType.GreaterThan => Expression.GreaterThan(memberExp, Expression.Constant(fieldRule.Value, memberExp.Type)),
// >=
ExpressionType.GreaterThanOrEqual => Expression.GreaterThanOrEqual(memberExp, Expression.Constant(fieldRule.Value, memberExp.Type)),
// 其他操作符未引入
_ => throw new NotSupportedException($"Dynamic rules do not support operator: {fieldRule.Operator}"),
};
switch (fieldRule.Logic)
{
case PredicateOperator.And:
where = where.And(Expression.Lambda<Func<T, bool>>(memberCondition, pe));
break;
case PredicateOperator.Or:
where = where.Or(Expression.Lambda<Func<T, bool>>(memberCondition, pe));
break;
}
}
queryable = queryable.Where(where);
}
return queryable;
}
protected virtual IQueryable<T> FilterFieldReturn<T>(IQueryable<T> queryable, IEnumerable<ProtectedField> fields)
{
// 默认未指定可访问字段则返回所有字段
if (fields.Any())
{
ParameterExpression pe = Expression.Parameter(typeof(T));
Type queryableResultType = typeof(T);
NewExpression ne = Expression.New(queryableResultType);
List<MemberAssignment> members = new List<MemberAssignment>();
foreach (var field in fields)
{
var fieldProp = queryableResultType.GetProperty(field.Field);
var meField = Expression.MakeMemberAccess(pe, fieldProp);
members.Add(Expression.Bind(fieldProp, meField));
}
var mie = Expression.MemberInit(ne, members);
Expression<Func<T, T>> personSelectExpression = Expression.Lambda<Func<T, T>>(mie, pe);
queryable = queryable.Select(personSelectExpression);
}
return queryable;
}
}
}

4
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection.EntityFrameworkCore/LINGYUN/Abp/DataProtection/EntityFrameworkCore/ProtectedEntityHelper.cs

@ -6,8 +6,8 @@ namespace LINGYUN.Abp.DataProtection.EntityFrameworkCore
public static class ProtectedEntityHelper
{
public static void TrySetOwner(
IDataProtection protectedEntity,
Func<string> ownerFactory)
IHasDataAccess protectedEntity,
Func<DataAccessOwner> ownerFactory)
{
ObjectHelper.TrySetProperty(
protectedEntity,

6
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN.Abp.DataProtection.csproj

@ -9,7 +9,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Threading" Version="$(VoloAbpPackageVersion)" />
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\authorization\LINGYUN.Abp.Authorization.OrganizationUnits\LINGYUN.Abp.Authorization.OrganizationUnits.csproj" />
</ItemGroup>
</Project>

15
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/AbpDataProtectionModule.cs

@ -1,11 +1,10 @@
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
using Volo.Abp.Domain;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.DataProtection
namespace LINGYUN.Abp.DataProtection;
[DependsOn(
typeof(AbpDddDomainModule))]
public class AbpDataProtectionModule : AbpModule
{
[DependsOn(
typeof(AbpThreadingModule))]
public class AbpDataProtectionModule : AbpModule
{
}
}

30
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessCacheItem.cs

@ -0,0 +1,30 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.DataProtection;
public class DataAccessCacheItem
{
/// <summary>
/// 实体类型全名
/// </summary>
public string EntityTypeFullName { get; set; }
/// <summary>
/// 数据访问操作
/// </summary>
public DataAccessOperation Operation { get; set; }
/// <summary>
/// 数据访问角色
/// </summary>
public DataAccessRole Role { get; set; }
/// <summary>
/// 资源访问者
/// </summary>
public string Visitor { get; set; }
/// <summary>
/// 字段访问控制
/// </summary>
public List<DataAccessFiledRule> Fileds { get; set; }
/// <summary>
/// 自定义表达式
/// </summary>
public List<DataAccessExpressionRule> Expressions { get; set; }
}

12
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessEntityRule.cs

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace LINGYUN.Abp.DataProtection;
/// <summary>
/// 数据访问实体控制
/// </summary>
public class DataAccessEntityRule
{
}

11
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessExpressionRule.cs

@ -0,0 +1,11 @@
namespace LINGYUN.Abp.DataProtection;
/// <summary>
/// 数据访问控制规则:自定义表达式
/// </summary>
public class DataAccessExpressionRule
{
/// <summary>
/// 表达式
/// </summary>
public virtual string Expression { get; protected set; }
}

19
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessFiledRule.cs

@ -0,0 +1,19 @@
namespace LINGYUN.Abp.DataProtection;
/// <summary>
/// 数据访问控制规则:字段
/// </summary>
public class DataAccessFiledRule
{
/// <summary>
/// 字段名称
/// </summary>
public virtual string Field { get; protected set; }
public DataAccessFiledRule()
{
}
public DataAccessFiledRule(string field)
{
Field = field;
}
}

7
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessOperation.cs

@ -0,0 +1,7 @@
namespace LINGYUN.Abp.DataProtection;
public enum DataAccessOperation
{
Read,
Write,
Delete
}

34
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessOwner.cs

@ -0,0 +1,34 @@
using System;
using System.Linq;
using Volo.Abp.Auditing;
namespace LINGYUN.Abp.DataProtection;
/// <summary>
/// 数据拥有者
/// </summary>
public class DataAccessOwner : IMayHaveCreator
{
public Guid? CreatorId { get; set; }
public string[] Roles { get; set; }
public string[] OrganizaztionUnits { get; set; }
protected DataAccessOwner()
{
}
public DataAccessOwner(Guid? creatorId, string[] roles, string[] organizaztionUnits)
{
CreatorId = creatorId;
Roles = roles;
OrganizaztionUnits = organizaztionUnits;
}
public bool IsInRole(string[] roles)
{
return Roles != null && Roles.Any(r => roles.Contains(r));
}
public bool IsInOrganizaztionUnit(string[] organizaztionUnits)
{
return OrganizaztionUnits != null && OrganizaztionUnits.Any(ou => organizaztionUnits.Contains(ou));
}
}

23
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessRole.cs

@ -0,0 +1,23 @@
namespace LINGYUN.Abp.DataProtection;
/// <summary>
/// 数据访问角色
/// </summary>
public enum DataAccessRole
{
/// <summary>
/// 所有
/// </summary>
All = 0,
/// <summary>
/// 用户
/// </summary>
User = 1,
/// <summary>
/// 角色
/// </summary>
Role = 2,
/// <summary>
/// 组织机构
/// </summary>
OrganizationUnit = 3,
}

54
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessRule.cs

@ -0,0 +1,54 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.DataProtection;
/// <summary>
/// 数据访问控制规则
/// </summary>
public class DataAccessRule
{
/// <summary>
/// 实体类型全名
/// </summary>
public virtual string EntityTypeFullName { get; protected set; }
/// <summary>
/// 数据访问操作
/// </summary>
public virtual DataAccessOperation Operation { get; protected set; }
/// <summary>
/// 数据访问角色
/// </summary>
public virtual DataAccessRole Role { get; protected set; }
/// <summary>
/// 资源访问者
/// </summary>
public virtual string Visitor { get; protected set; }
/// <summary>
/// 字段访问控制
/// </summary>
public virtual List<DataAccessFiledRule> Fileds { get; protected set; }
/// <summary>
/// 自定义表达式
/// </summary>
public virtual List<DataAccessExpressionRule> Expressions { get; protected set; }
protected DataAccessRule()
{
Fileds = [];
Expressions = [];
}
public DataAccessRule(
string entityTypeFullName,
DataAccessOperation operation = DataAccessOperation.Read,
DataAccessRole role = DataAccessRole.All,
string visitor = null,
List<DataAccessFiledRule> fileds = null,
List<DataAccessExpressionRule> expressions = null)
{
EntityTypeFullName = entityTypeFullName;
Operation = operation;
Role = role;
Visitor = visitor;
Fileds = fileds ?? [];
Expressions = expressions ?? [];
}
}

11
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataAccessRuleInfo.cs

@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.DataProtection;
public class DataAccessRuleInfo
{
public List<DataAccessRule> Rules { get; }
public DataAccessRuleInfo(List<DataAccessRule> rules)
{
Rules = rules ?? new List<DataAccessRule>();
}
}

352
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/DataProtectionAsyncQueryableProvider.cs

@ -1,352 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Linq;
namespace LINGYUN.Abp.DataProtection
{
public class DataProtectionAsyncQueryableProvider : IAsyncQueryableProvider
{
public Task<bool> AllAsync<T>(
IQueryable<T> queryable,
Expression<Func<T, bool>> predicate,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<bool> AnyAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<bool> AnyAsync<T>(IQueryable<T> queryable, Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<decimal> AverageAsync(IQueryable<decimal> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<decimal?> AverageAsync(IQueryable<decimal?> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<decimal> AverageAsync<T>(IQueryable<T> queryable, Expression<Func<T, decimal>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<decimal?> AverageAsync<T>(IQueryable<T> queryable, Expression<Func<T, decimal?>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double> AverageAsync(IQueryable<int> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double?> AverageAsync(IQueryable<int?> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double> AverageAsync<T>(IQueryable<T> queryable, Expression<Func<T, int>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double?> AverageAsync<T>(IQueryable<T> queryable, Expression<Func<T, int?>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double> AverageAsync(IQueryable<long> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double?> AverageAsync(IQueryable<long?> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double> AverageAsync<T>(IQueryable<T> queryable, Expression<Func<T, long>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double?> AverageAsync<T>(IQueryable<T> queryable, Expression<Func<T, long?>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double> AverageAsync(IQueryable<double> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double?> AverageAsync(IQueryable<double?> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double> AverageAsync<T>(IQueryable<T> queryable, Expression<Func<T, double>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double?> AverageAsync<T>(IQueryable<T> queryable, Expression<Func<T, double?>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<float> AverageAsync(IQueryable<float> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<float?> AverageAsync(IQueryable<float?> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<float> AverageAsync<T>(IQueryable<T> queryable, Expression<Func<T, float>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<float?> AverageAsync<T>(IQueryable<T> queryable, Expression<Func<T, float?>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public bool CanExecute<T>(IQueryable<T> queryable)
{
throw new NotImplementedException();
}
public Task<bool> ContainsAsync<T>(IQueryable<T> queryable, T item, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<int> CountAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<int> CountAsync<T>(IQueryable<T> queryable, Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<T> FirstAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<T> FirstAsync<T>(IQueryable<T> queryable, Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<T> FirstOrDefaultAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<T> FirstOrDefaultAsync<T>(IQueryable<T> queryable, Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<T> LastAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<T> LastAsync<T>(IQueryable<T> queryable, Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<T> LastOrDefaultAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<T> LastOrDefaultAsync<T>(IQueryable<T> queryable, Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<long> LongCountAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<long> LongCountAsync<T>(IQueryable<T> queryable, Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<T> MaxAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<TResult> MaxAsync<T, TResult>(IQueryable<T> queryable, Expression<Func<T, TResult>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<T> MinAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<TResult> MinAsync<T, TResult>(IQueryable<T> queryable, Expression<Func<T, TResult>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<T> SingleAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<T> SingleAsync<T>(IQueryable<T> queryable, Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<T> SingleOrDefaultAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<T> SingleOrDefaultAsync<T>(IQueryable<T> queryable, Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<decimal> SumAsync(IQueryable<decimal> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<decimal?> SumAsync(IQueryable<decimal?> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<decimal> SumAsync<T>(IQueryable<T> queryable, Expression<Func<T, decimal>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<decimal?> SumAsync<T>(IQueryable<T> queryable, Expression<Func<T, decimal?>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<int> SumAsync(IQueryable<int> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<int?> SumAsync(IQueryable<int?> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<int> SumAsync<T>(IQueryable<T> queryable, Expression<Func<T, int>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<int?> SumAsync<T>(IQueryable<T> queryable, Expression<Func<T, int?>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<long> SumAsync(IQueryable<long> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<long?> SumAsync(IQueryable<long?> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<long> SumAsync<T>(IQueryable<T> queryable, Expression<Func<T, long>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<long?> SumAsync<T>(IQueryable<T> queryable, Expression<Func<T, long?>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double> SumAsync(IQueryable<double> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double?> SumAsync(IQueryable<double?> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double> SumAsync<T>(IQueryable<T> queryable, Expression<Func<T, double>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<double?> SumAsync<T>(IQueryable<T> queryable, Expression<Func<T, double?>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<float> SumAsync(IQueryable<float> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<float?> SumAsync(IQueryable<float?> source, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<float> SumAsync<T>(IQueryable<T> queryable, Expression<Func<T, float>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<float?> SumAsync<T>(IQueryable<T> queryable, Expression<Func<T, float?>> selector, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<T[]> ToArrayAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task<List<T>> ToListAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
}
}

12
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/ExpressionType.cs

@ -1,12 +0,0 @@
namespace LINGYUN.Abp.DataProtection
{
public enum ExpressionType
{
Contains,
Equal,
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual,
}
}

8
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/IDataAccessCache.cs

@ -0,0 +1,8 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace LINGYUN.Abp.DataProtection;
public interface IDataAccessCache
{
}

19
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/IDataProtectdChecker.cs

@ -1,19 +0,0 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.DataProtection
{
/// <summary>
/// 实现此接口
/// 检查资源的访问权限
/// </summary>
public interface IDataProtectdChecker
{
/// <summary>
/// 资源是否拥有某种行为的访问权限
/// </summary>
/// <typeparam name="T">受保护的资源(实体)</typeparam>
/// <param name="behavior">访问行为</param>
/// <returns>不管是否拥有访问权限,请返回非空结果,由EF模块检查</returns>
Task<ResourceGrantedResult> IsGrantedAsync<T>(ProtectBehavior behavior = ProtectBehavior.All);
}
}

11
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/IDataProtection.cs

@ -1,11 +0,0 @@
namespace LINGYUN.Abp.DataProtection
{
/// <summary>
/// 实现接口
/// 数据访问保护
/// </summary>
public interface IDataProtection
{
string Owner { get; }
}
}

5
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/IHasDataAccess.cs

@ -0,0 +1,5 @@
namespace LINGYUN.Abp.DataProtection;
public interface IHasDataAccess
{
public DataAccessOwner Owner { get; }
}

29
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/ProtectBehavior.cs

@ -1,29 +0,0 @@
namespace LINGYUN.Abp.DataProtection
{
/// <summary>
/// 保护行为
/// </summary>
public enum ProtectBehavior
{
/// <summary>
/// 所有
/// </summary>
All,
/// <summary>
/// 查询
/// </summary>
Query,
/// <summary>
/// 新增
/// </summary>
Insert,
/// <summary>
/// 修改
/// </summary>
Update,
/// <summary>
/// 删除
/// </summary>
Delete
}
}

22
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/ProtectedField.cs

@ -1,22 +0,0 @@
namespace LINGYUN.Abp.DataProtection
{
public class ProtectedField
{
/// <summary>
/// 资源
/// </summary>
public string Resource { get; set; }
/// <summary>
/// 资源拥有者
/// </summary>
public string Owner { get; set; }
/// <summary>
/// 资源访问者
/// </summary>
public string Visitor { get; set; }
/// <summary>
/// 字段
/// </summary>
public string Field { get; set; }
}
}

40
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/ProtectedFieldRule.cs

@ -1,40 +0,0 @@
using System;
using System.Linq;
using System.Linq.Expressions;
namespace LINGYUN.Abp.DataProtection
{
public class ProtectedFieldRule
{
/// <summary>
/// 资源
/// </summary>
public string Resource { get; set; }
/// <summary>
/// 资源拥有者
/// </summary>
public string Owner { get; set; }
/// <summary>
/// 资源访问者
/// </summary>
public string Visitor { get; set; }
/// <summary>
/// 字段
/// </summary>
public string Field { get; set; }
/// <summary>
/// 值
/// </summary>
public object Value { get; set; }
/// <summary>
/// 连接类型
/// Or 或
/// And 且
/// </summary>
public PredicateOperator Logic { get; set; }
/// <summary>
/// 操作符
/// </summary>
public ExpressionType Operator { get; set; }
}
}

27
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/ProtectedResource.cs

@ -1,27 +0,0 @@
namespace LINGYUN.Abp.DataProtection
{
public class ProtectedResource
{
/// <summary>
/// 资源
/// </summary>
public string Resource { get; set; }
/// <summary>
/// 资源拥有者
/// </summary>
public string Owner { get; set; }
/// <summary>
/// 资源访问者
/// </summary>
public string Visitor { get; set; }
/// <summary>
/// 优先级
/// 值越大排名越靠前
/// </summary>
public int Priority { get; set; }
/// <summary>
/// 行为
/// </summary>
public ProtectBehavior Behavior { get; set; }
}
}

19
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/LINGYUN/Abp/DataProtection/ResourceGrantedResult.cs

@ -1,19 +0,0 @@
namespace LINGYUN.Abp.DataProtection
{
public class ResourceGrantedResult
{
public bool Succeeded => Resource != null;
public ProtectedResource Resource { get; }
public ProtectedField[] Fields { get; }
public ProtectedFieldRule[] Rules { get; }
public ResourceGrantedResult(
ProtectedResource resource,
ProtectedField[] fields,
ProtectedFieldRule[] rules)
{
Resource = resource;
Fields = fields;
Rules = rules;
}
}
}

24
aspnet-core/framework/data-protection/LINGYUN.Abp.DataProtection/Volo/Abp/Uow/IUnitOfWorkDataAccessExtensions.cs

@ -0,0 +1,24 @@
using LINGYUN.Abp.DataProtection;
namespace Volo.Abp.Uow;
public static class IUnitOfWorkDataAccessExtensions
{
private const string DataAccessRuleKey = "LINGYUN.Abp.DataProtection.DataAccess";
public static IUnitOfWork SetAccessRuleInfo(
this IUnitOfWork unitOfWork,
DataAccessRuleInfo dataAccessRuleInfo)
{
unitOfWork.RemoveItem(DataAccessRuleKey);
unitOfWork.AddItem(DataAccessRuleKey, dataAccessRuleInfo);
return unitOfWork;
}
public static DataAccessRuleInfo GetAccessRuleInfo(
this IUnitOfWork unitOfWork)
{
return unitOfWork.GetItemOrDefault<DataAccessRuleInfo>(DataAccessRuleKey)
?? new DataAccessRuleInfo(null);
}
}

2
aspnet-core/tests/LINGYUN.Abp.DataProtection.Tests/LINGYUN/Abp/DataProtection/AbpDataProtectionTestModule.cs

@ -12,7 +12,7 @@ namespace LINGYUN.Abp.DataProtection
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<FakeDataProtectedDbContext>(options =>
context.Services.AddAbpDbContext<AbpDataProtectionDbContext>(options =>
{
options.AddRepository<FakeProtectionObject, EfCoreFakeProtectionObjectRepository>();
});

5
aspnet-core/tests/LINGYUN.Abp.DataProtection.Tests/LINGYUN/Abp/DataProtection/EfCoreFakeProtectionObjectRepository.cs

@ -1,14 +1,15 @@
using LINGYUN.Abp.DataProtection.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Abp.DataProtection
{
public class EfCoreFakeProtectionObjectRepository :
EfCoreDataProtectionRepositoryBase<FakeDataProtectedDbContext, FakeProtectionObject, int>,
EfCoreRepository<AbpDataProtectionDbContext, FakeProtectionObject, int>,
IFakeProtectionObjectRepository
{
public EfCoreFakeProtectionObjectRepository(
IDbContextProvider<FakeDataProtectedDbContext> dbContextProvider)
IDbContextProvider<AbpDataProtectionDbContext> dbContextProvider)
: base(dbContextProvider)
{
}

25
aspnet-core/tests/LINGYUN.Abp.DataProtection.Tests/LINGYUN/Abp/DataProtection/FakeDataProtectdChecker.cs

@ -1,25 +0,0 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Uow;
namespace LINGYUN.Abp.DataProtection
{
public class FakeDataProtectdChecker : IDataProtectdChecker, ISingletonDependency
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
public FakeDataProtectdChecker(IUnitOfWorkManager unitOfWorkManager)
{
_unitOfWorkManager = unitOfWorkManager;
}
public virtual Task<ResourceGrantedResult> IsGrantedAsync<T>(ProtectBehavior behavior = ProtectBehavior.All)
{
var cacheItem = _unitOfWorkManager.Current.Items["ResourceGranted"];
var result = cacheItem.As<ResourceGrantedResult>();
return Task.FromResult(result);
}
}
}

24
aspnet-core/tests/LINGYUN.Abp.DataProtection.Tests/LINGYUN/Abp/DataProtection/FakeDataProtectedDbContext.cs

@ -1,24 +0,0 @@
using LINGYUN.Abp.DataProtection.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace LINGYUN.Abp.DataProtection
{
public class FakeDataProtectedDbContext : AbpDataProtectionDbContext<FakeDataProtectedDbContext>
{
public FakeDataProtectedDbContext(
DbContextOptions<FakeDataProtectedDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<FakeProtectionObject>(b =>
{
b.Property(p => p.Owner).HasColumnName(nameof(IDataProtection.Owner)).HasMaxLength(200);
});
}
}
}

4
aspnet-core/tests/LINGYUN.Abp.DataProtection.Tests/LINGYUN/Abp/DataProtection/FakeProtectionObject.cs

@ -2,9 +2,8 @@
namespace LINGYUN.Abp.DataProtection
{
public class FakeProtectionObject : Entity<int>, IDataProtection
public class FakeProtectionObject : Entity<int>, IHasDataAccess
{
public virtual string Owner { get; set; }
public virtual string Protect1 { get; set; }
public virtual string Protect2 { get; set; }
public virtual string Value1 { get; set; }
@ -12,5 +11,6 @@ namespace LINGYUN.Abp.DataProtection
public virtual int ProtectNum1 { get; set; }
public virtual int ProtectNum2 { get; set; }
public virtual int Num3 { get; set; }
public DataAccessOwner Owner { get; set; }
}
}

309
aspnet-core/tests/LINGYUN.Abp.DataProtection.Tests/LINGYUN/Abp/DataProtection/ProtectionFieldTests.cs

@ -1,12 +1,11 @@
using System.Collections.Generic;
using System.Linq;
using Shouldly;
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Xunit;
using Volo.Abp.Security.Claims;
using System.Security.Claims;
using System;
using Shouldly;
using Volo.Abp.Uow;
using Xunit;
namespace LINGYUN.Abp.DataProtection
{
@ -71,74 +70,62 @@ namespace LINGYUN.Abp.DataProtection
await WithUnitOfWorkAsync(async () =>
{
var resource = new ProtectedResource
{
Resource = typeof(FakeProtectionObject).FullName,
Behavior = ProtectBehavior.All,
Owner = "user1",
Priority = 10,
Visitor = "user1,role1"
};
var fields = new List<ProtectedField>()
{
new ProtectedField
{
Field = nameof(FakeProtectionObject.Num3),
Owner = "user1",
Resource = resource.Resource,
Visitor = "",
},
new ProtectedField
{
Field = nameof(FakeProtectionObject.Value1),
Owner = "user2",
Resource = resource.Resource,
Visitor = "",
},
new ProtectedField
{
Field = nameof(FakeProtectionObject.Value2),
Owner = "user1",
Resource = resource.Resource,
Visitor = "",
},
new ProtectedField
{
Field = nameof(FakeProtectionObject.Protect1),
Owner = "role1",
Resource = resource.Resource,
Visitor = "",
},
};
var rules = new List<ProtectedFieldRule>()
{
new ProtectedFieldRule
{
Field = nameof(FakeProtectionObject.Protect1),
Logic = PredicateOperator.And,
Operator = ExpressionType.Equal,
Resource = resource.Resource,
Value = "test"
},
new ProtectedFieldRule
{
Field = nameof(FakeProtectionObject.Num3),
Logic = PredicateOperator.Or,
Operator = ExpressionType.LessThanOrEqual,
Resource = resource.Resource,
Value = 300
},
};
var user1Rule = new DataAccessRule(
typeof(FakeProtectionObject).FullName,
DataAccessOperation.Write,
DataAccessRole.User,
"user1",
[
new(nameof(FakeProtectionObject.Num3)),
new(nameof(FakeProtectionObject.Value2)),
]);
var user2Rule = new DataAccessRule(
typeof(FakeProtectionObject).FullName,
DataAccessOperation.Write,
DataAccessRole.User,
"user2",
[
new(nameof(FakeProtectionObject.Value1)),
]);
var role1Rule = new DataAccessRule(
typeof(FakeProtectionObject).FullName,
DataAccessOperation.Write,
DataAccessRole.Role,
"role1",
[
new(nameof(FakeProtectionObject.Protect1)),
]);
var dataAccessRule = new DataAccessRuleInfo(
[
user1Rule,
user2Rule,
role1Rule,
]);
//var rules = new List<ProtectedFieldRule>()
//{
// new ProtectedFieldRule
// {
// Field = nameof(FakeProtectionObject.Protect1),
// Logic = PredicateOperator.And,
// Operator = ExpressionType.Equal,
// Resource = resource.Resource,
// Value = "test"
// },
// new ProtectedFieldRule
// {
// Field = nameof(FakeProtectionObject.Num3),
// Logic = PredicateOperator.Or,
// Operator = ExpressionType.LessThanOrEqual,
// Resource = resource.Resource,
// Value = 300
// },
//};
var unitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
unitOfWorkManager.Current.AddItem<ResourceGrantedResult>(
"ResourceGranted",
new ResourceGrantedResult(
resource,
fields.ToArray(),
rules.ToArray()));
unitOfWorkManager.Current.SetAccessRuleInfo(dataAccessRule);
var identity = new ClaimsIdentity();
identity.AddClaim(new Claim(AbpClaimTypes.UserId, Guid.NewGuid().ToString()));
@ -153,74 +140,61 @@ namespace LINGYUN.Abp.DataProtection
await WithUnitOfWorkAsync(async () =>
{
var resource = new ProtectedResource
{
Resource = typeof(FakeProtectionObject).FullName,
Behavior = ProtectBehavior.All,
Owner = "user1",
Priority = 10,
Visitor = "user1,role1"
};
var fields = new List<ProtectedField>()
{
new ProtectedField
{
Field = nameof(FakeProtectionObject.Num3),
Owner = "user1",
Resource = resource.Resource,
Visitor = "",
},
new ProtectedField
{
Field = nameof(FakeProtectionObject.Value1),
Owner = "user2",
Resource = resource.Resource,
Visitor = "",
},
new ProtectedField
{
Field = nameof(FakeProtectionObject.Value2),
Owner = "user1",
Resource = resource.Resource,
Visitor = "",
},
new ProtectedField
{
Field = nameof(FakeProtectionObject.Protect1),
Owner = "role1",
Resource = resource.Resource,
Visitor = "",
},
};
var rules = new List<ProtectedFieldRule>()
{
new ProtectedFieldRule
{
Field = nameof(FakeProtectionObject.Protect1),
Logic = PredicateOperator.And,
Operator = ExpressionType.Equal,
Resource = resource.Resource,
Value = "test"
},
new ProtectedFieldRule
{
Field = nameof(FakeProtectionObject.Num3),
Logic = PredicateOperator.Or,
Operator = ExpressionType.LessThanOrEqual,
Resource = resource.Resource,
Value = 300
},
};
var user1Rule = new DataAccessRule(
typeof(FakeProtectionObject).FullName,
DataAccessOperation.Read,
DataAccessRole.User,
"user1",
[
new(nameof(FakeProtectionObject.Num3)),
new(nameof(FakeProtectionObject.Value2)),
]);
var user2Rule = new DataAccessRule(
typeof(FakeProtectionObject).FullName,
DataAccessOperation.Write,
DataAccessRole.User,
"user2",
[
new(nameof(FakeProtectionObject.Value1)),
]);
var role1Rule = new DataAccessRule(
typeof(FakeProtectionObject).FullName,
DataAccessOperation.Write,
DataAccessRole.Role,
"role1",
[
new(nameof(FakeProtectionObject.Protect1)),
]);
var dataAccessRule = new DataAccessRuleInfo(
[
user1Rule,
user2Rule,
role1Rule,
]);
//var rules = new List<ProtectedFieldRule>()
//{
// new ProtectedFieldRule
// {
// Field = nameof(FakeProtectionObject.Protect1),
// Logic = PredicateOperator.And,
// Operator = ExpressionType.Equal,
// Resource = resource.Resource,
// Value = "test"
// },
// new ProtectedFieldRule
// {
// Field = nameof(FakeProtectionObject.Num3),
// Logic = PredicateOperator.Or,
// Operator = ExpressionType.LessThanOrEqual,
// Resource = resource.Resource,
// Value = 300
// },
//};
var unitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
unitOfWorkManager.Current.AddItem<ResourceGrantedResult>(
"ResourceGranted",
new ResourceGrantedResult(
resource,
fields.ToArray(),
rules.ToArray()));
unitOfWorkManager.Current.SetAccessRuleInfo(dataAccessRule);
var identity2 = new ClaimsIdentity();
identity2.AddClaim(new Claim(AbpClaimTypes.UserId, Guid.NewGuid().ToString()));
@ -235,52 +209,23 @@ namespace LINGYUN.Abp.DataProtection
await WithUnitOfWorkAsync(async () =>
{
var resource = new ProtectedResource
{
Resource = typeof(FakeProtectionObject).FullName,
Behavior = ProtectBehavior.All,
Priority = 10,
Visitor = "user3"
};
var fields = new List<ProtectedField>()
{
new ProtectedField
{
Field = nameof(FakeProtectionObject.Num3),
Owner = "user1",
Resource = resource.Resource,
Visitor = "",
}
};
var rules = new List<ProtectedFieldRule>()
{
new ProtectedFieldRule
{
Field = nameof(FakeProtectionObject.Protect1),
Logic = PredicateOperator.And,
Operator = ExpressionType.Equal,
Resource = resource.Resource,
Value = "test"
},
new ProtectedFieldRule
{
Field = nameof(FakeProtectionObject.Num3),
Logic = PredicateOperator.Or,
Operator = ExpressionType.LessThanOrEqual,
Resource = resource.Resource,
Value = 300
},
};
var user1Rule = new DataAccessRule(
typeof(FakeProtectionObject).FullName,
DataAccessOperation.Read,
DataAccessRole.User,
"user3",
[
new(nameof(FakeProtectionObject.Num3)),
new(nameof(FakeProtectionObject.Value2)),
]);
var dataAccessRule = new DataAccessRuleInfo(
[
user1Rule,
]);
var unitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
unitOfWorkManager.Current.AddItem<ResourceGrantedResult>(
"ResourceGranted",
new ResourceGrantedResult(
resource,
fields.ToArray(),
rules.ToArray()));
unitOfWorkManager.Current.SetAccessRuleInfo(dataAccessRule);
var identity3 = new ClaimsIdentity();
identity3.AddClaim(new Claim(AbpClaimTypes.UserId, Guid.NewGuid().ToString()));

Loading…
Cancel
Save