data_scope.py 5.8 KB

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