delete_guard.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. """删除权限:仅创建人可删(super_admin 例外)。"""
  2. from __future__ import annotations
  3. from typing import Any, Dict, Iterable, Optional, Union
  4. from fastapi import HTTPException
  5. RowLike = Union[Any, Dict[str, Any]]
  6. def normalize_user_info(
  7. user_info: Optional[Dict[str, Any]] = None,
  8. *,
  9. user_id: Optional[str] = None,
  10. actor_name: Optional[str] = None,
  11. ) -> Dict[str, Any]:
  12. info: Dict[str, Any] = dict(user_info or {})
  13. uid = (user_id or info.get("user_id") or info.get("id") or "").strip()
  14. if uid:
  15. info["user_id"] = uid
  16. name = (actor_name or info.get("real_name") or info.get("username") or info.get("userName") or info.get("name") or "").strip()
  17. if name:
  18. info["real_name"] = name
  19. return info
  20. def user_info_from_context(ctx_or_dict: Any) -> Dict[str, Any]:
  21. if isinstance(ctx_or_dict, dict):
  22. return normalize_user_info(ctx_or_dict)
  23. info: Dict[str, Any] = {}
  24. if hasattr(ctx_or_dict, "to_user_info"):
  25. info.update(ctx_or_dict.to_user_info())
  26. name = (getattr(ctx_or_dict, "name", None) or "").strip()
  27. if name:
  28. info["real_name"] = name
  29. return normalize_user_info(info)
  30. def is_super_admin(user_info: Optional[Dict[str, Any]]) -> bool:
  31. if not user_info:
  32. return False
  33. return (user_info.get("role") or "").strip() == "super_admin"
  34. def row_creator_id(row: RowLike) -> str:
  35. if row is None:
  36. return ""
  37. if isinstance(row, dict):
  38. return (row.get("create_by") or row.get("createBy") or "").strip()
  39. return (getattr(row, "create_by", None) or getattr(row, "createBy", None) or "").strip()
  40. def _actor_ids(user_info: Optional[Dict[str, Any]]) -> set[str]:
  41. if not user_info:
  42. return set()
  43. ids: set[str] = set()
  44. for key in ("user_id", "id"):
  45. val = (user_info.get(key) or "").strip()
  46. if val:
  47. ids.add(val)
  48. return ids
  49. def _actor_names(user_info: Optional[Dict[str, Any]]) -> set[str]:
  50. if not user_info:
  51. return set()
  52. names: set[str] = set()
  53. for key in ("real_name", "username", "userName", "name"):
  54. val = (user_info.get(key) or "").strip()
  55. if val:
  56. names.add(val)
  57. return names
  58. def can_delete_by_creator(
  59. row: RowLike,
  60. user_info: Optional[Dict[str, Any]] = None,
  61. *,
  62. user_id: Optional[str] = None,
  63. actor_name: Optional[str] = None,
  64. ) -> bool:
  65. info = normalize_user_info(user_info, user_id=user_id, actor_name=actor_name)
  66. if is_super_admin(info):
  67. return True
  68. creator = row_creator_id(row)
  69. if not creator:
  70. return False
  71. if creator in _actor_ids(info):
  72. return True
  73. if creator in _actor_names(info):
  74. return True
  75. return False
  76. def assert_row_deletable_by_creator(
  77. row: RowLike,
  78. user_info: Optional[Dict[str, Any]] = None,
  79. *,
  80. user_id: Optional[str] = None,
  81. actor_name: Optional[str] = None,
  82. message: str = "仅创建人可删除",
  83. ) -> None:
  84. if can_delete_by_creator(row, user_info, user_id=user_id, actor_name=actor_name):
  85. return
  86. raise HTTPException(status_code=403, detail=message)
  87. def assert_rows_deletable_by_creator(
  88. rows: Iterable[RowLike],
  89. user_info: Optional[Dict[str, Any]] = None,
  90. *,
  91. user_id: Optional[str] = None,
  92. actor_name: Optional[str] = None,
  93. message: str = "仅创建人可删除",
  94. ) -> None:
  95. info = normalize_user_info(user_info, user_id=user_id, actor_name=actor_name)
  96. for row in rows:
  97. assert_row_deletable_by_creator(row, info, message=message)