""" 入库模块字段级权限严格映射 (Default Deny) 规则: - None = 基础字段,始终保留 - 字符串 = 需要用户拥有该精确权限码,否则移除 - 不在映射中的字段 → 立即删除 (Default Deny) """ STOCK_FIELD_RBAC_MAPPING = { "MaterialBase": { "id": None, "isEnabled": None, "visibilityLevel": None, "name": "material_list:name", "commonName": "material_list:commonName", "category": "material_list:category", "type": "material_list:type", "spec": "material_list:spec", "unit": "material_list:unit", "companyName": "material_list:companyName", "isInspectionRequired": "material_list:isInspectionRequired", "generalImage": "material_list:files", "generalManual": "material_list:files", "productImageRemark": "material_list:productImageRemark", "manualLinkRemark": "material_list:manualLinkRemark", "referencePrice": "material_list:referencePrice", "inventoryCount": "material_list:inventoryCount", "availableCount": "material_list:availableCount", }, "StockBuy": { "id": None, "request_id": None, "request_no": None, "in_quantity": None, "stock_quantity": None, "available_quantity": None, "warehouse_loc": None, "status": None, "global_print_id": None, "company_name": "material_list:companyName", "material_name": "material_list:name", "spec_model": "material_list:spec", "category": "material_list:category", "unit": "material_list:unit", "material_type": "material_list:type", "isInspectionRequired": "material_list:isInspectionRequired", "sku": "inbound_buy:sku", "inbound_date": "inbound_buy:inbound_date", "barcode": "inbound_buy:barcode", "serial_number": "inbound_buy:sn_bn", "batch_number": "inbound_buy:sn_bn", "inspection_status": "inbound_buy:inspection_status", "unit_price": "inbound_buy:unit_price", "post_tax_unit_price": "inbound_buy:post_tax_unit_price", "total_price": "inbound_buy:total_price", "tax_rate": "inbound_buy:tax_rate", "currency": "inbound_buy:currency", "exchange_rate": "inbound_buy:exchange_rate", "supplier_name": "inbound_buy:supplier_name", "purchaser": "inbound_buy:purchaser", "purchaser_email": "inbound_buy:purchaser_email", "source_link": "inbound_buy:source_link", "detail_link": "inbound_buy:detail_link", "arrival_photo": "inbound_buy:arrival_photo", "inspection_report": "inbound_buy:inspection_report", }, "StockSemi": { "id": None, "in_quantity": None, "stock_quantity": None, "available_quantity": None, "warehouse_loc": None, "status": None, "global_print_id": None, "company_name": "material_list:companyName", "material_name": "material_list:name", "spec_model": "material_list:spec", "category": "material_list:category", "unit": "material_list:unit", "material_type": "material_list:type", "sku": "inbound_semi:sku", "inbound_date": "inbound_semi:inbound_date", "barcode": "inbound_semi:barcode", "serial_number": "inbound_semi:sn_bn", "batch_number": "inbound_semi:sn_bn", "bom_code": "inbound_semi:bom_code", "bom_version": "inbound_semi:bom_version", "work_order_code": "inbound_semi:work_order_code", "raw_material_cost": "inbound_semi:raw_material_cost", "manual_cost": "inbound_semi:manual_cost", "unit_total_cost": "inbound_semi:unit_total_cost", "total_price": "inbound_semi:total_price", "production_manager": "inbound_semi:production_manager", "production_time_range": "inbound_semi:production_time_range", "production_start_time": "inbound_semi:production_start_time", "production_end_time": "inbound_semi:production_end_time", "quality_status": "inbound_semi:quality_status", "quality_report_link": "inbound_semi:quality_report_link", "arrival_photo": "inbound_semi:arrival_photo", "remark": "inbound_semi:remark", "detail_link": "inbound_semi:detail_link", }, "StockProduct": { "id": None, "in_quantity": None, "stock_quantity": None, "available_quantity": None, "warehouse_loc": None, "status": None, "global_print_id": None, "company_name": "material_list:companyName", "material_name": "material_list:name", "spec_model": "material_list:spec", "category": "material_list:category", "unit": "material_list:unit", "material_type": "material_list:type", "sku": "inbound_product:sku", "inbound_date": "inbound_product:inbound_date", "barcode": "inbound_product:barcode", "serial_number": "inbound_product:serial_number", "bom_code": "inbound_product:bom_code", "bom_version": "inbound_product:bom_version", "work_order_code": "inbound_product:work_order_code", "raw_material_cost": "inbound_product:raw_material_cost", "manual_cost": "inbound_product:manual_cost", "unit_total_cost": "inbound_product:unit_total_cost", "production_manager": "inbound_product:production_manager", "production_time_range": "inbound_product:production_time_range", "quality_status": "inbound_product:quality_status", "quality_report_link": "inbound_product:quality_report_link", "inspection_report_link": "inbound_product:inspection_report_link", "sale_price": "inbound_product:sale_price", "order_id": "inbound_product:order_id", "product_photo": "inbound_product:product_photo", "remark": "inbound_product:remark", "detail_link": "inbound_product:detail_link", }, } def _is_super_admin(user_permissions: list) -> bool: """判断是否为超级管理员(支持 * 和 module:* 两种通配符)""" return '*' in user_permissions or any(p.endswith(':*') for p in user_permissions) def apply_strict_rbac(item_dict: dict, table_name: str, user_permissions: list) -> dict: """ Default Deny 字段过滤器: 1. 不在映射中的 key → 立即删除 2. 在映射中但需要权限且用户没有 → 删除 3. 在映射中且为 None 或用户有权限 → 保留 """ mapping = STOCK_FIELD_RBAC_MAPPING.get(table_name) if not mapping: return item_dict # 未知表不做过滤 # 超级管理员放行(支持 * / module:* 通配符) if _is_super_admin(user_permissions): for key in list(item_dict.keys()): if key not in mapping: del item_dict[key] return item_dict for key in list(item_dict.keys()): if key not in mapping: del item_dict[key] # Default Deny else: perm_code = mapping[key] if perm_code is not None and perm_code not in user_permissions: if isinstance(item_dict[key], (int, float)): item_dict[key] = 0 elif isinstance(item_dict[key], bool): item_dict[key] = False else: item_dict[key] = None return item_dict