PayrollManagementSystem/backend/app/services/authorization_policy.py
liuwenxin 68e70fd6e2 fix(权限): 修复业务接入预览权限映射
业务接入预览属于只读预演,不应要求执行导入权限。\n将预览接口映射到 tplus:preview,并增加路由权限回归,避免只读角色被错误拒绝。\n\n影响范围:T+ 工作台业务接入预览接口与权限策略。\n\nCo-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-05 19:31:01 +08:00

97 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""每个受保护接口必须映射权限;未登记接口默认拒绝。"""
RESOURCE_MODULES = {
"base.personnel": "base:personnel", "base.products": "base:products",
"base.customers": "base:customers", "business.businesses": "business:businesses",
"business.shipments": "business:shipments", "business.receipts": "business:receipts",
"business.ratings": "business:ratings", "system.users": "system:users",
"system.roles": "system:roles", "system.business_roles": "system:business_roles",
"system.logs": "system:logs", "system.settings": "system:settings",
"system.monitor": "system:monitor",
}
REPORT_MODULES = {
"personal-detail": "reports:personal", "department-summary": "reports:department",
"business-stats": "reports:business", "payment-stats": "reports:payments",
}
PUBLIC_ENDPOINTS = {("auth", "get_captcha"), ("auth", "login"),
("system.settings", "get_settings_public"), ("system.settings", "get_logo_public")}
LOGIN_ENDPOINTS = {("auth", "me"), ("auth", "logout")}
def permission_for(module, name, method, *, params=None, body=None):
"""返回目录操作码;None 表示没有登记,空串仅用于显式登录态接口。"""
params, body = params or {}, body or {}
if (module, name) in PUBLIC_ENDPOINTS | LOGIN_ENDPOINTS:
return ""
if module == "dashboard":
return "dashboard:view"
if module == "system.authorization":
resource = "system:users" if name in ("user_authorization", "preview", "save_user") else "system:roles"
return resource + (":view" if method == "GET" or name == "preview" else ":update")
if module == "metadata":
return "" # 此端点按请求的参照资源执行独立的关联功能校验。
if module == "base.business_roles":
return "" # 下拉只返回选项;由端点校验人员查看/维护能力。
if module in RESOURCE_MODULES:
resource = RESOURCE_MODULES[module]
if module == "base.products" and name in ("extraction_preview", "extraction_apply", "restore_source"):
return resource + (":create" if name == "extraction_apply" else ":update" if name == "restore_source" else ":view")
if method == "GET":
return resource + ":view"
special = {"set_status": "status", "change_status": "status", "reset_password": "password",
"confirm_receipt": "confirm",
"replace_regions": "regions", "replace_plans": "plans", "rate_customer": "rating",
"change_ownership": "ownership", "batch_ownership": "ownership", "upload_logo": "update"}
action = special.get(name) or {"POST": "create", "PUT": "update", "DELETE": "delete"}.get(method)
return resource + ":" + action if action else None
if module == "system.params":
action = "view" if method == "GET" else {"submit_change": "update", "withdraw_batch": "withdraw", "preview": "preview"}.get(name)
if name == "export_changes":
action = "export"
return "system:params:" + action if action else None
if module == "system.coefficients":
return "system:params:" + ({"save": "update", "export": "export", "import_preview": "preview"}.get(name, "view"))
if module == "system.calc_templates":
return "system:params:" + ("view" if method == "GET" else "update")
if module == "system.monitor":
return "system:monitor:" + ("export" if name == "diagnostic" else "view")
if module == "system.tplus":
return "tplus:" + ("view" if method == "GET" else "retry" if name == "retry" else "collect")
if module == "system.tplus_workbench":
if name == "business_sync_preview":
return "tplus:preview"
if name.startswith("business_sync_"):
return "tplus:view" if method == "GET" else "tplus:import"
if name == "work_report":
return "tplus:export"
if method == "GET":
return "tplus:view"
if name in ("save_fact", "save_correction_pair"):
return "tplus:facts"
if name == "create_work":
return "tplus:import" if body.get("kind") in ("MASTER_APPLY", "BUSINESS_APPLY") else "tplus:preview"
if module in ("payroll", "calc"):
if name == "probation_adjustment":
return "adjustments:create"
if name in ("probation_export", "salary_export"):
return "reports:personal:export"
if method == "GET":
return "payroll:view"
return "adjustments:create" if name == "adjustment" else "payroll:prepare" if name == "save_input" else "payroll:calculate"
if module == "settlements":
if name in ("create_adjustment", "list_adjustments"):
return "adjustments:" + ("view" if method == "GET" else "create")
return "settlements:" + ("view" if method == "GET" else "generate" if name == "generate" else "submit")
if module == "system.approvals":
return "approvals:view" # 节点动作由服务层根据当前节点检查 finance/leader。
if module == "reports":
code = params.get("code") if name == "export_report" else {
"personal_detail": "personal-detail", "department_summary": "department-summary",
"business_stats": "business-stats", "payment_stats": "payment-stats",
"payment_detail": "payment-stats", "payment_day_buckets": "payment-stats",
}.get(name)
resource = REPORT_MODULES.get(code)
return resource + (":export" if name == "export_report" else ":view") if resource else None
return None