data_scope.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. """数据权限过滤(V0.3:基于角色 data_scope 的查询级行级过滤)
  2. data_scope 值:
  3. 1 = 全部数据
  4. 2 = 本部门数据
  5. 3 = 本部门及以下数据
  6. 4 = 仅本人数据
  7. 5 = 自定义(sys_role_data_scope_detail)
  8. """
  9. from __future__ import annotations
  10. from typing import List, Set, Tuple
  11. from sqlalchemy import or_
  12. from sqlalchemy.orm import Session
  13. from app.core.config import settings
  14. from app.models.models import (
  15. SysDept,
  16. SysRole,
  17. SysRoleDataScopeDetail,
  18. SysUser,
  19. )
  20. def get_user_dept_id(db: Session, user_id: str) -> str | None:
  21. """从 SysUser 读取用户的 dept_id。"""
  22. row = (
  23. db.query(SysUser.dept_id)
  24. .filter(SysUser.id == user_id, SysUser.del_flag == 0)
  25. .first()
  26. )
  27. return row[0] if row and row[0] else None
  28. def _get_child_dept_ids(db: Session, dept_id: str, tenant_no: str) -> List[str]:
  29. """递归获取 dept_id 及其所有子部门 ID(含自身)。"""
  30. result: List[str] = [dept_id]
  31. # 用循环逐层扩展,避免深度递归栈溢出
  32. queue: List[str] = [dept_id]
  33. while queue:
  34. parents = queue
  35. queue = []
  36. children = (
  37. db.query(SysDept.id)
  38. .filter(
  39. SysDept.parent_id.in_(parents),
  40. SysDept.del_flag == 0,
  41. SysDept.tenant_no == tenant_no,
  42. )
  43. .all()
  44. )
  45. for child in children:
  46. cid = child[0]
  47. if cid not in result:
  48. result.append(cid)
  49. queue.append(cid)
  50. return result
  51. def _resolve_user_role_scopes(
  52. db: Session, user_id: str, tenant_no: str
  53. ) -> List[dict]:
  54. """获取用户有效角色的数据权限配置(直接角色 ∪ 职位继承角色)。
  55. 返回: [{"data_scope": str, "details": [SysRoleDataScopeDetail, ...]}]
  56. """
  57. from app.services.web_auth_roles import get_effective_role_ids
  58. role_ids = get_effective_role_ids(db, user_id, tenant_no=tenant_no)
  59. if not role_ids:
  60. return []
  61. roles = (
  62. db.query(SysRole)
  63. .filter(
  64. SysRole.id.in_(role_ids),
  65. SysRole.del_flag == 0,
  66. SysRole.status == "1",
  67. SysRole.tenant_no == tenant_no,
  68. )
  69. .all()
  70. )
  71. if not roles:
  72. return []
  73. result: List[dict] = []
  74. # 批量查所有自定义权限明细
  75. all_details = (
  76. db.query(SysRoleDataScopeDetail)
  77. .filter(
  78. SysRoleDataScopeDetail.role_id.in_(role_ids),
  79. SysRoleDataScopeDetail.del_flag == 0,
  80. SysRoleDataScopeDetail.tenant_no == tenant_no,
  81. )
  82. .all()
  83. )
  84. details_by_role: dict[str, list] = {}
  85. for d in all_details:
  86. details_by_role.setdefault(d.role_id, []).append(d)
  87. for role in roles:
  88. result.append(
  89. {
  90. "data_scope": role.data_scope or "1",
  91. "details": details_by_role.get(role.id, []),
  92. }
  93. )
  94. return result
  95. def apply_data_scope(query, model, user_info: dict, db: Session, tenant_no: str = None):
  96. """根据用户角色的数据权限过滤查询。
  97. Args:
  98. query: SQLAlchemy query 对象
  99. model: 查询的主模型类(需要有 create_dept_id / create_by 字段)
  100. user_info: 当前用户信息字典,需含 role / user_id / dept_id 等
  101. db: 数据库 Session
  102. tenant_no: 租户编号,默认使用 settings.DEFAULT_TENANT_NO
  103. Returns:
  104. 应用数据权限过滤后的 query
  105. """
  106. # 1. 无用户信息 → 不过滤
  107. if not user_info:
  108. return query
  109. # 2. super_admin 直接放行
  110. if user_info.get("role") == "super_admin":
  111. return query
  112. # 3. 获取 user_id
  113. user_id = user_info.get("user_id")
  114. if not user_id:
  115. return query
  116. # 4. 确定 tenant_no
  117. effective_tenant = tenant_no or settings.DEFAULT_TENANT_NO
  118. # 5. 检查 model 是否有可过滤字段
  119. has_dept = hasattr(model, "create_dept_id")
  120. has_user = hasattr(model, "create_by")
  121. if not has_dept and not has_user:
  122. return query
  123. # 6. 获取用户所有角色的数据权限
  124. role_scopes = _resolve_user_role_scopes(db, user_id, effective_tenant)
  125. if not role_scopes:
  126. return query.filter(False)
  127. # 7. 任一角色拥有全部数据权限 → 放行(union 语义)
  128. if any(rs["data_scope"] == "1" for rs in role_scopes):
  129. return query
  130. # 8. 收集所有角色的 dept_ids 和 user_ids(union 合并)
  131. dept_ids: Set[str] = set()
  132. user_ids: Set[str] = set()
  133. # 获取用户部门
  134. user_dept_id = user_info.get("dept_id") or user_info.get("region_id") or get_user_dept_id(db, user_id)
  135. for rs in role_scopes:
  136. scope = rs["data_scope"]
  137. if scope == "2":
  138. # 本部门
  139. if user_dept_id:
  140. dept_ids.add(user_dept_id)
  141. elif scope == "3":
  142. # 本部门及以下
  143. if user_dept_id:
  144. dept_ids.update(_get_child_dept_ids(db, user_dept_id, effective_tenant))
  145. elif scope == "4":
  146. # 仅本人
  147. user_ids.add(user_id)
  148. elif scope == "5":
  149. # 自定义
  150. for detail in rs["details"]:
  151. val = (detail.scope_value or "").strip()
  152. if not val:
  153. continue
  154. if detail.scope_type == "dept":
  155. dept_ids.add(val)
  156. elif detail.scope_type == "user":
  157. user_ids.add(val)
  158. # 9. 构建 OR 条件并应用
  159. conditions = []
  160. if dept_ids and has_dept:
  161. conditions.append(model.create_dept_id.in_(dept_ids))
  162. if user_ids and has_user:
  163. conditions.append(model.create_by.in_(user_ids))
  164. if not conditions:
  165. # 没有任何可匹配条件 → 返回空结果
  166. return query.filter(False)
  167. return query.filter(or_(*conditions))