fix(backend): 权限漏洞修复 + 业务逻辑审查修复

1. 宏观状态越权修复 (products.py + product_service.py):
   - update_overall_status 增加权限校验:仅 SUPER_ADMIN 或当前操作该产品主线任务的人可修改,否则 403

2. 任务撤回越权修复 (task_service.py + tasks.py):
   - recall_task 增加校验:操作人必须等于上游任务负责人(谁发出的谁撤回),否则 403
   - 管理员 (SUPER_ADMIN/SUPERVISOR) 直接放行

3. 驳回上游溯源修复 (task_service.py - reject_task):
   - 将 TaskLog (action_type=create) 提升为优先溯源方式,解决协助分支转交后驳回找不到正确发起人的 Bug
   - 保留 parent_task.assignee_id + task.assignee_id 两级兜底

4. complete_task 父节点继承修复:
   - 修复 task_type == 'MAIN' 永不匹配的 Bug(模型无此值)
   - 改为 not parent_task_id or task_type in (TRANSFER, RECOVERY)
This commit is contained in:
2026-08-11 15:05:30 +08:00
parent d40a8d480e
commit 88dc7381f6
4 changed files with 120 additions and 22 deletions

View File

@ -275,8 +275,17 @@ async def update_product(db: AsyncSession, product_id: uuid.UUID, data: ProductU
VALID_OVERALL_STATUS = {"备货", "生产", "测试", "维修", "在库"}
async def update_overall_status(db: AsyncSession, serial_number: str, status_value: str) -> ProductScanResponse:
"""更新产品宏观状态"""
async def update_overall_status(
db: AsyncSession, serial_number: str, status_value: str,
current_user: dict | None = None,
) -> ProductScanResponse:
"""更新产品宏观状态
权限校验:
- SUPER_ADMIN 角色:直接放行
- 当前操作该产品主线任务(WIP/PENDING 状态主干任务)的人:放行
- 其他:403
"""
if status_value not in VALID_OVERALL_STATUS:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
@ -292,6 +301,38 @@ async def update_overall_status(db: AsyncSession, serial_number: str, status_val
if not product:
raise HTTPException(status_code=404, detail=f"未找到序列号 {serial_number} 的产品")
# ── 权限校验 ──
if current_user:
user_role = current_user.get("role", "")
user_username = current_user.get("username", "")
# SUPER_ADMIN 直接放行
if user_role == "SUPER_ADMIN":
pass
else:
# 检查当前用户是否是该产品主线任务的负责人
from sqlalchemy import or_
main_task_result = await db.execute(
select(Task).where(
Task.product_id == product.id,
Task.status.in_(["WIP", "PENDING"]),
or_(
Task.parent_task_id.is_(None),
Task.task_type.in_(["TRANSFER", "RECOVERY"]),
),
).order_by(Task.created_at.desc()).limit(1)
)
main_task = main_task_result.scalar_one_or_none()
has_permission = (
main_task
and main_task.assignee_id == user_username
)
if not has_permission:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="只有 SUPER_ADMIN 或当前操作该产品主线任务的人才能修改宏观状态",
)
product.overall_status = status_value
await db.commit()
await db.refresh(product)