From fd084bf8fdc558e1b24ba8aa67bccb53a599101c Mon Sep 17 00:00:00 2001 From: yueli Date: Thu, 17 Sep 2026 11:07:57 +0800 Subject: [PATCH] =?UTF-8?q?fix(material):=20=E9=99=84=E4=BB=B6=E5=A4=87?= =?UTF-8?q?=E6=B3=A8=E7=BA=B3=E5=85=A5=E5=86=99=E6=9D=83=E9=99=90=E7=AE=A1?= =?UTF-8?q?=E6=8E=A7=EF=BC=8C=E5=BA=9F=E6=AD=A2=E3=80=8C=E4=B8=8D=E5=9C=A8?= =?UTF-8?q?=E6=98=A0=E5=B0=84=E4=B8=AD=E2=86=92=E9=BB=98=E8=AE=A4=E5=85=81?= =?UTF-8?q?=E8=AE=B8=E3=80=8D=E7=9A=84=E5=85=9C=E5=BA=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题 ---- POST /inbound/base/ 与 PUT /inbound/base/ 的 field_to_perm 里**没有** productImageRemark / manualLinkRemark,于是这两个字段落入 「不在映射中 → 默认允许」的兜底分支 —— 任何能调通该接口的角色都能改。 配合读侧引用幽灵权限码,形成「谁都能写、除超管没人能读」的错位。 改动 ---- 两处映射(创建 + 修改)同时显式补入: 'productImageRemark': 'material_list:remark_edit', 'manualLinkRemark': 'material_list:remark_edit', 无写权限者的请求不会携带该字段进入服务层 —— 是「丢弃本次修改」而非 「写成空值」,因此不会误清既有内容。 ★ 超管不受影响:base.py 的 get_current_user_permissions() 对超管返回的 硬编码列表以 'material_list:*' 开头,命中通配符分支后整段过滤被跳过。 验证(6 个角色 × 读写,12 项断言全通过) 超管 / 主管 / 库管 → 读✓ 写✓ 入库 / 出库 / 销售 → 读✓ 写✗ --- inventory-backend/app/api/v1/inbound/base.py | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/inventory-backend/app/api/v1/inbound/base.py b/inventory-backend/app/api/v1/inbound/base.py index 4ae1bbd..6f9a5d5 100644 --- a/inventory-backend/app/api/v1/inbound/base.py +++ b/inventory-backend/app/api/v1/inbound/base.py @@ -264,6 +264,18 @@ def create(): 'availableCount': 'material_list:availableCount', 'generalManual': 'material_list:files', 'generalImage': 'material_list:files', + # ★ 附件备注的写权限单独收紧(原先这两个字段**不在映射中**,落入 + # 下面「不在映射中 → 默认允许」的兜底分支 —— 任何能调通本接口的 + # 角色都能改;而读侧又要求一个从未注册过的权限码,造成 + # 「谁都能写、除超管没人能读」的读写错位。现双向对齐: + # 读 → material_list:productImageRemark / manualLinkRemark(6 角色) + # 写 → material_list:remark_edit(3 个核心管理角色) + # 无写权限的请求不会携带该字段进入服务层,于是既不改动它、 + # 也不会写成空值 —— 是「丢弃本次修改」而非「误清既有内容」。 + # ⚠ 该码只做精确匹配,**不要**用 @permission_required 包裹 —— + # 会被 _expand_operation_perms 前缀桥接放大给 material_list:operation。 + 'productImageRemark': 'material_list:remark_edit', + 'manualLinkRemark': 'material_list:remark_edit', 'referencePrice': 'material_list:referencePrice', 'isEnabled': 'material_list:isEnabled' } @@ -319,6 +331,18 @@ def update(id): 'availableCount': 'material_list:availableCount', 'generalManual': 'material_list:files', 'generalImage': 'material_list:files', + # ★ 附件备注的写权限单独收紧(原先这两个字段**不在映射中**,落入 + # 下面「不在映射中 → 默认允许」的兜底分支 —— 任何能调通本接口的 + # 角色都能改;而读侧又要求一个从未注册过的权限码,造成 + # 「谁都能写、除超管没人能读」的读写错位。现双向对齐: + # 读 → material_list:productImageRemark / manualLinkRemark(6 角色) + # 写 → material_list:remark_edit(3 个核心管理角色) + # 无写权限的请求不会携带该字段进入服务层,于是既不改动它、 + # 也不会写成空值 —— 是「丢弃本次修改」而非「误清既有内容」。 + # ⚠ 该码只做精确匹配,**不要**用 @permission_required 包裹 —— + # 会被 _expand_operation_perms 前缀桥接放大给 material_list:operation。 + 'productImageRemark': 'material_list:remark_edit', + 'manualLinkRemark': 'material_list:remark_edit', 'referencePrice': 'material_list:referencePrice', 'isEnabled': 'material_list:isEnabled' }