# -*- coding: utf-8 -*- """pbl_blueprint RBAC 路径注册清单(显式枚举,无 %/* 通配符)。 本文件是 RBAC 路径的**唯一声明源**。规则: 1. 每条路径都是完整字面量,以 /api/ 开头、以 .dspy 结尾; 2. 严禁 % / * 通配符(RBAC 必须逐条授权,不可模糊匹配); 3. 严禁重复; 4. 必须**覆盖** pbl_blueprint/json/*.json 中 editable 声明的全部 3×11 条路径 (selfcheck.py F 组逐条比对,缺一条即退出码非 0); 5. 表名以设计定稿 B1~B11 为权威 (projects/pbls/docs/01-design/modules/pbl_blueprint.md §2)。 用法: python3 modules/pbl_blueprint/scripts/load_path.py # 打印清单 python3 modules/pbl_blueprint/scripts/load_path.py --check # 与 json editable 比对 """ import json import os import sys MODULE_NAME = "pbl_blueprint" REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) JSON_DIR = os.path.join(REPO_ROOT, "pbl_blueprint", "json") # 设计定稿 B1~B11 主清单(权威表名,顺序即编号) MAIN_TABLES = ( "pbl_blueprint", # B1 聚合根 "pbl_blueprint_version", # B2 版本(append-only) "pbl_blueprint_approval", # B3 审批记录 "pbl_learner", # B4 学习者画像 "pbl_learning_goal", # B5 学习目标 "pbl_problem", # B6 真实世界问题 "pbl_driving_question", # B7 驱动问题 "pbl_project", # B8 项目(constraints) "pbl_role", # B9 角色 "pbl_mission", # B10 任务 "pbl_artifact_def", # B11 产出物定义 ) # 运维支撑表(在途已有实现,归档保留,路径仍需授权) SUPPORT_TABLES = ( "pbl_blueprint_audit", "pbl_blueprint_edge", "pbl_blueprint_fork", "pbl_blueprint_lock", "pbl_blueprint_node", "pbl_blueprint_offline", "pbl_blueprint_publish", "pbl_blueprint_template", "pbl_blueprint_version_delta", ) # ---------------------------------------------------------------- B1 聚合根 PATHS_BLUEPRINT = ( "/api/pbl_blueprint/list.dspy", "/api/pbl_blueprint/edit.dspy", "/api/pbl_blueprint/view.dspy", "/api/pbl_blueprint/create.dspy", "/api/pbl_blueprint/get.dspy", "/api/pbl_blueprint/update.dspy", "/api/pbl_blueprint/delete.dspy", "/api/pbl_blueprint/tree.dspy", "/api/pbl_blueprint/fork.dspy", "/api/pbl_blueprint/forks.dspy", "/api/pbl_blueprint/stats.dspy", "/api/pbl_blueprint/change_status.dspy", "/api/pbl_blueprint/set_quality_level.dspy", "/api/pbl_blueprint/submit_approval.dspy", "/api/pbl_blueprint/approve.dspy", "/api/pbl_blueprint/reject.dspy", ) # ---------------------------------------------------------------- B2 版本 PATHS_VERSION = ( "/api/pbl_blueprint_version/list.dspy", "/api/pbl_blueprint_version/edit.dspy", "/api/pbl_blueprint_version/view.dspy", "/api/pbl_blueprint_version/get.dspy", "/api/pbl_blueprint_version/commit.dspy", "/api/pbl_blueprint_version/delta.dspy", "/api/pbl_blueprint_version/rollback.dspy", ) # ---------------------------------------------------------------- B3 审批 PATHS_APPROVAL = ( "/api/pbl_blueprint_approval/list.dspy", "/api/pbl_blueprint_approval/edit.dspy", "/api/pbl_blueprint_approval/view.dspy", "/api/pbl_blueprint_approval/get.dspy", "/api/pbl_blueprint_approval/create.dspy", "/api/pbl_blueprint_approval/update.dspy", "/api/pbl_blueprint_approval/delete.dspy", ) # ------------------------------------------- B4~B11 七类子对象(统一 5 动作 CRUD) _SUBOBJECT_ACTIONS = ("list", "edit", "view", "create", "get", "update", "delete") PATHS_LEARNER = tuple( "/api/pbl_learner/%s.dspy" % a for a in _SUBOBJECT_ACTIONS) PATHS_LEARNING_GOAL = tuple( "/api/pbl_learning_goal/%s.dspy" % a for a in _SUBOBJECT_ACTIONS) PATHS_PROBLEM = tuple( "/api/pbl_problem/%s.dspy" % a for a in _SUBOBJECT_ACTIONS) PATHS_DRIVING_QUESTION = tuple( "/api/pbl_driving_question/%s.dspy" % a for a in _SUBOBJECT_ACTIONS) PATHS_PROJECT = tuple( "/api/pbl_project/%s.dspy" % a for a in _SUBOBJECT_ACTIONS) PATHS_ROLE = tuple( "/api/pbl_role/%s.dspy" % a for a in _SUBOBJECT_ACTIONS) PATHS_MISSION = tuple( "/api/pbl_mission/%s.dspy" % a for a in _SUBOBJECT_ACTIONS) PATHS_ARTIFACT_DEF = tuple( "/api/pbl_artifact_def/%s.dspy" % a for a in _SUBOBJECT_ACTIONS) # ---------------------------------------------------------------- 运维支撑表 PATHS_SUPPORT = ( # 审计(append-only,只读) "/api/pbl_blueprint_audit/list.dspy", "/api/pbl_blueprint_audit/edit.dspy", "/api/pbl_blueprint_audit/view.dspy", "/api/pbl_blueprint_audit/get.dspy", # 关系边 "/api/pbl_blueprint_edge/list.dspy", "/api/pbl_blueprint_edge/edit.dspy", "/api/pbl_blueprint_edge/view.dspy", "/api/pbl_blueprint_edge/create.dspy", "/api/pbl_blueprint_edge/get.dspy", "/api/pbl_blueprint_edge/update.dspy", "/api/pbl_blueprint_edge/delete.dspy", # fork 溯源 "/api/pbl_blueprint_fork/list.dspy", "/api/pbl_blueprint_fork/edit.dspy", "/api/pbl_blueprint_fork/view.dspy", "/api/pbl_blueprint_fork/get.dspy", "/api/pbl_blueprint_fork/lineage.dspy", # 编辑锁 "/api/pbl_blueprint_lock/list.dspy", "/api/pbl_blueprint_lock/edit.dspy", "/api/pbl_blueprint_lock/view.dspy", "/api/pbl_blueprint_lock/lock.dspy", "/api/pbl_blueprint_lock/unlock.dspy", "/api/pbl_blueprint_lock/clean_expired.dspy", # 泛化节点(在途实现) "/api/pbl_blueprint_node/list.dspy", "/api/pbl_blueprint_node/edit.dspy", "/api/pbl_blueprint_node/view.dspy", "/api/pbl_blueprint_node/create.dspy", "/api/pbl_blueprint_node/get.dspy", "/api/pbl_blueprint_node/update.dspy", "/api/pbl_blueprint_node/delete.dspy", # 离线包 "/api/pbl_blueprint_offline/list.dspy", "/api/pbl_blueprint_offline/edit.dspy", "/api/pbl_blueprint_offline/view.dspy", "/api/pbl_blueprint_offline/get.dspy", "/api/pbl_blueprint_offline/export.dspy", "/api/pbl_blueprint_offline/import.dspy", # 发布 "/api/pbl_blueprint_publish/list.dspy", "/api/pbl_blueprint_publish/edit.dspy", "/api/pbl_blueprint_publish/view.dspy", "/api/pbl_blueprint_publish/get.dspy", "/api/pbl_blueprint_publish/create.dspy", "/api/pbl_blueprint_publish/revoke.dspy", # 模板 "/api/pbl_blueprint_template/list.dspy", "/api/pbl_blueprint_template/edit.dspy", "/api/pbl_blueprint_template/view.dspy", "/api/pbl_blueprint_template/create.dspy", "/api/pbl_blueprint_template/get.dspy", "/api/pbl_blueprint_template/update.dspy", "/api/pbl_blueprint_template/delete.dspy", "/api/pbl_blueprint_template/instantiate.dspy", # 版本增量明细 "/api/pbl_blueprint_version_delta/list.dspy", "/api/pbl_blueprint_version_delta/edit.dspy", "/api/pbl_blueprint_version_delta/view.dspy", "/api/pbl_blueprint_version_delta/get.dspy", ) # ---------------------------------------------------------------- 汇总(唯一出口) PATHS = ( PATHS_BLUEPRINT + PATHS_VERSION + PATHS_APPROVAL + PATHS_LEARNER + PATHS_LEARNING_GOAL + PATHS_PROBLEM + PATHS_DRIVING_QUESTION + PATHS_PROJECT + PATHS_ROLE + PATHS_MISSION + PATHS_ARTIFACT_DEF + PATHS_SUPPORT ) def json_editable_paths(): """读取 json/*.json 的 editable,返回其对应的 /api/ 路径集合。""" out = set() if not os.path.isdir(JSON_DIR): return out for fn in sorted(os.listdir(JSON_DIR)): if not fn.endswith(".json"): continue try: with open(os.path.join(JSON_DIR, fn), "r", encoding="utf-8") as f: d = json.load(f) except Exception: # noqa: BLE001 continue for u in (d.get("editable") or []): if isinstance(u, str) and u.startswith("api/"): out.add("/" + u) return out def validate(): """自检:无通配符 / 全部 /api/ 开头 / 无重复 / 覆盖 json editable。""" errs = [] wild = [p for p in PATHS if "%" in p or "*" in p] if wild: errs.append("存在通配路径: {}".format(wild)) bad = [p for p in PATHS if not p.startswith("/api/") or not p.endswith(".dspy")] if bad: errs.append("非法路径格式: {}".format(bad)) dup = sorted({p for p in PATHS if PATHS.count(p) > 1}) if dup: errs.append("重复路径: {}".format(dup)) need = json_editable_paths() miss = sorted(need - set(PATHS)) if miss: errs.append("未覆盖 json editable 路径: {}".format(miss)) return errs def main(argv): if "--check" in argv: errs = validate() if errs: print("RBAC 路径校验失败:") for e in errs: print(" - " + e) return 1 print("RBAC 路径校验通过: {} 条(无通配符/无重复/覆盖 json editable {} 条)".format( len(PATHS), len(json_editable_paths()))) return 0 print("# {} RBAC 路径清单({} 条,显式枚举)".format(MODULE_NAME, len(PATHS))) for p in PATHS: print(p) return 0 if __name__ == "__main__": sys.exit(main(sys.argv[1:]))