mirror of
https://github.com/fastapi-practices/fastapi_best_architecture.git
synced 2026-03-13 09:31:31 +08:00
Optimize the data permission condition build (#904)
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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='创建时间')
|
||||
|
||||
@@ -47,4 +47,4 @@ class GetDataScopeDetail(DataScopeBase):
|
||||
class GetDataScopeWithRelationDetail(GetDataScopeDetail):
|
||||
"""数据范围关联详情"""
|
||||
|
||||
rules: list[GetDataRuleDetail] = Field([], description='数据规则列表')
|
||||
rules: list[GetDataRuleDetail | None] = Field([], description='数据规则列表')
|
||||
|
||||
@@ -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='数据范围列表')
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user