Optimize the data permission condition build (#904)

This commit is contained in:
Wu Clan
2025-11-08 16:35:15 +08:00
committed by GitHub
parent 292f5d7e48
commit 0ea213edfd
8 changed files with 29 additions and 26 deletions

View File

@@ -63,7 +63,7 @@ class CRUDDept(CRUDPlus[Dept]):
if status is not None:
filters['status'] = status
data_filtered = await filter_data_permission(db, request_user)
data_filtered = filter_data_permission(request_user)
return await self.select_models_order(db, 'sort', 'desc', data_filtered, **filters)
async def create(self, db: AsyncSession, obj: CreateDeptParam) -> None:

View File

@@ -6,7 +6,7 @@ from sqlalchemy.orm import noload, selectinload
from sqlalchemy.sql import Select
from sqlalchemy_crud_plus import CRUDPlus
from backend.app.admin.model import Dept, Role, User
from backend.app.admin.model import DataScope, Dept, Role, User
from backend.app.admin.schema.user import (
AddOAuth2UserParam,
AddUserParam,
@@ -281,7 +281,12 @@ class CRUDUser(CRUDPlus[User]):
return await self.select_model_by_column(
db,
load_options=[selectinload(self.model.roles).options(selectinload(Role.menus), selectinload(Role.scopes))],
load_options=[
selectinload(self.model.roles).options(
selectinload(Role.menus),
selectinload(Role.scopes).options(selectinload(DataScope.rules)),
)
],
load_strategies=['dept'],
**filters,
)

View File

@@ -34,7 +34,7 @@ class DeleteDataRuleParam(SchemaBase):
class GetDataRuleDetail(DataRuleSchemaBase):
"""数据规则详情"""
model_config = ConfigDict(from_attributes=True)
model_config = ConfigDict(from_attributes=True, frozen=True)
id: int = Field(description='规则 ID')
created_time: datetime = Field(description='创建时间')

View File

@@ -47,4 +47,4 @@ class GetDataScopeDetail(DataScopeBase):
class GetDataScopeWithRelationDetail(GetDataScopeDetail):
"""数据范围关联详情"""
rules: list[GetDataRuleDetail] = Field([], description='数据规则列表')
rules: list[GetDataRuleDetail | None] = Field([], description='数据规则列表')

View File

@@ -2,7 +2,7 @@ from datetime import datetime
from pydantic import ConfigDict, Field
from backend.app.admin.schema.data_scope import GetDataScopeDetail
from backend.app.admin.schema.data_scope import GetDataScopeWithRelationDetail
from backend.app.admin.schema.menu import GetMenuDetail
from backend.common.enums import StatusType
from backend.common.schema import SchemaBase
@@ -57,4 +57,4 @@ class GetRoleWithRelationDetail(GetRoleDetail):
"""角色关联详情"""
menus: list[GetMenuDetail | None] = Field([], description='菜单详情列表')
scopes: list[GetDataScopeDetail | None] = Field([], description='数据范围列表')
scopes: list[GetDataScopeWithRelationDetail | None] = Field([], description='数据范围列表')

View File

@@ -1,8 +1,6 @@
from fastapi import Request
from sqlalchemy import ColumnElement, and_, or_
from sqlalchemy.ext.asyncio import AsyncSession
from backend.app.admin.crud.crud_data_scope import data_scope_dao
from backend.app.admin.schema.user import GetUserInfoWithRelationDetail
from backend.common.context import ctx
from backend.common.enums import RoleDataRuleExpressionType, RoleDataRuleOperatorType
@@ -43,14 +41,13 @@ class RequestPermission:
ctx.permission = self.value
async def filter_data_permission(db: AsyncSession, request_user: GetUserInfoWithRelationDetail) -> ColumnElement[bool]: # noqa: C901
def filter_data_permission(request_user: GetUserInfoWithRelationDetail) -> ColumnElement[bool]: # noqa: C901
"""
过滤数据权限,控制用户可见数据范围
使用场景:
- 控制用户能看到哪些数据
:param db: 数据库会话
:param request_user: 请求用户
:return:
"""
@@ -62,31 +59,21 @@ async def filter_data_permission(db: AsyncSession, request_user: GetUserInfoWith
if not role.is_filter_scopes:
return or_(1 == 1)
# 获取数据范围
data_scope_ids = set()
# 获取数据规则
data_rules = set()
for role in request_user.roles:
for scope in role.scopes:
if scope.status:
data_scope_ids.add(scope.id)
data_rules.update(scope.rules)
# 无规则用户不做过滤
if not list(data_scope_ids):
if not list(data_rules):
return or_(1 == 1)
# 获取数据范围规则
unique_data_rules = {}
for data_scope_id in list(data_scope_ids):
data_scope_with_relation = await data_scope_dao.get_with_relation(db, data_scope_id)
for rule in data_scope_with_relation.rules:
unique_data_rules[rule.id] = rule
# 转换为列表
data_rule_list = list(unique_data_rules.values())
where_and_list = []
where_or_list = []
for data_rule in data_rule_list:
for data_rule in list(data_rules):
# 验证规则模型
rule_model = data_rule.model
if rule_model not in settings.DATA_PERMISSION_MODELS:

View File

@@ -111,6 +111,11 @@ values
(1, '部门名称等于测试', '部门', 'name', 1, 0, '测试', now(), null),
(2, '父部门 ID 等于 1', '部门', 'parent_id', 0, 0, '1', now(), null);
insert into sys_role_data_scope (id, role_id, data_scope_id)
values
(1, 1, 1),
(2, 1, 2);
insert into sys_data_scope_rule (id, data_scope_id, data_rule_id)
values
(1, 1, 1),

View File

@@ -111,6 +111,11 @@ values
(1, '部门名称等于测试', '部门', 'name', 1, 0, '测试', now(), null),
(2, '父部门 ID 等于 1', '部门', 'parent_id', 0, 0, '1', now(), null);
insert into sys_role_data_scope (id, role_id, data_scope_id)
values
(1, 1, 1),
(2, 1, 2);
insert into sys_data_scope_rule (id, data_scope_id, data_rule_id)
values
(1, 1, 1),
@@ -125,4 +130,5 @@ select setval(pg_get_serial_sequence('sys_user', 'id'),coalesce(max(id), 0) + 1,
select setval(pg_get_serial_sequence('sys_user_role', 'id'),coalesce(max(id), 0) + 1, true) from sys_user_role;
select setval(pg_get_serial_sequence('sys_data_scope', 'id'),coalesce(max(id), 0) + 1, true) from sys_data_scope;
select setval(pg_get_serial_sequence('sys_data_rule', 'id'),coalesce(max(id), 0) + 1, true) from sys_data_rule;
select setval(pg_get_serial_sequence('sys_role_data_scope', 'id'),coalesce(max(id), 0) + 1, true) from sys_role_data_scope;
select setval(pg_get_serial_sequence('sys_data_scope_rule', 'id'),coalesce(max(id), 0) + 1, true) from sys_data_scope_rule;