pbl_blueprint/scripts/load_path.py

180 lines
5.6 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.

# -*- coding: utf-8 -*-
"""pbl_blueprint RBAC 路径注册清单(显式枚举,无 %/* 通配符)。
本文件是 RBAC 路径的唯一声明源init.py 的 RBAC_PATHS 必须与此处 PATHS 完全一致,
selfcheck.py 会逐条比对(不一致即退出码非 0
用法:
python3 modules/pbl_blueprint/scripts/load_path.py # 打印清单
python3 modules/pbl_blueprint/scripts/load_path.py --check # 与 init.py 比对
"""
import os
import sys
MODULE_NAME = "pbl_blueprint"
# 蓝图主表
PATHS_BLUEPRINT = (
"/api/pbl_blueprint/list.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",
)
# 子对象节点
PATHS_NODE = (
"/api/pbl_blueprint_node/list.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",
)
# 关系边
PATHS_EDGE = (
"/api/pbl_blueprint_edge/list.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",
)
# 版本与差异
PATHS_VERSION = (
"/api/pbl_blueprint_version/list.dspy",
"/api/pbl_blueprint_version/get.dspy",
"/api/pbl_blueprint_version/save.dspy",
"/api/pbl_blueprint_version/rollback.dspy",
"/api/pbl_blueprint_version_delta/list.dspy",
"/api/pbl_blueprint_version_delta/get.dspy",
)
# 模板
PATHS_TEMPLATE = (
"/api/pbl_blueprint_template/list.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",
)
# 发布
PATHS_PUBLISH = (
"/api/pbl_blueprint_publish/list.dspy",
"/api/pbl_blueprint_publish/create.dspy",
"/api/pbl_blueprint_publish/get.dspy",
"/api/pbl_blueprint_publish/revoke.dspy",
)
# 离线包
PATHS_OFFLINE = (
"/api/pbl_blueprint_offline/list.dspy",
"/api/pbl_blueprint_offline/export.dspy",
"/api/pbl_blueprint_offline/import.dspy",
"/api/pbl_blueprint_offline/get.dspy",
)
# 血缘
PATHS_FORK = (
"/api/pbl_blueprint_fork/list.dspy",
"/api/pbl_blueprint_fork/get.dspy",
)
# 编辑锁
PATHS_LOCK = (
"/api/pbl_blueprint_lock/lock.dspy",
"/api/pbl_blueprint_lock/unlock.dspy",
"/api/pbl_blueprint_lock/list.dspy",
"/api/pbl_blueprint_lock/clean_expired.dspy",
)
# 审计(只读)
PATHS_AUDIT = (
"/api/pbl_blueprint_audit/list.dspy",
"/api/pbl_blueprint_audit/get.dspy",
)
PATHS = (
PATHS_BLUEPRINT + PATHS_NODE + PATHS_EDGE + PATHS_VERSION + PATHS_TEMPLATE +
PATHS_PUBLISH + PATHS_OFFLINE + PATHS_FORK + PATHS_LOCK + PATHS_AUDIT
)
# 只读路径RBAC 上归为 read 权限组)
READONLY_PATHS = tuple(p for p in PATHS if p.rsplit("/", 1)[-1].split(".")[0] in
("list", "get", "tree", "forks", "stats"))
WRITE_PATHS = tuple(p for p in PATHS if p not in READONLY_PATHS)
def validate(paths=None):
"""校验路径合法性:非空、以 / 开头、.dspy 结尾、无通配符、无重复。返回错误列表。"""
ps = list(paths if paths is not None else PATHS)
errs = []
seen = set()
for p in ps:
if not p or not isinstance(p, str):
errs.append("空路径或非字符串: %r" % (p,))
continue
if not p.startswith("/api/"):
errs.append("路径必须以 /api/ 开头: %s" % p)
if not p.endswith(".dspy"):
errs.append("路径必须以 .dspy 结尾: %s" % p)
if "%" in p or "*" in p or "?" in p:
errs.append("路径含通配符(禁止): %s" % p)
if " " in p:
errs.append("路径含空格: %s" % p)
if p in seen:
errs.append("路径重复: %s" % p)
seen.add(p)
return errs
def check_against_init():
"""与 pbl_blueprint/init.py 的 RBAC_PATHS 逐条比对,返回 (ok, diff_msg)。"""
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if root not in sys.path:
sys.path.insert(0, root)
try:
from pbl_blueprint import init as _init # noqa
except Exception as e:
return False, "无法导入 pbl_blueprint.init: %s" % e
a = list(PATHS)
b = list(getattr(_init, "RBAC_PATHS", ()))
if a == b:
return True, "RBAC 路径一致(%d 条,顺序相同)" % len(a)
only_a = [x for x in a if x not in b]
only_b = [x for x in b if x not in a]
return False, "RBAC 路径不一致: 仅 load_path=%s / 仅 init=%s / 顺序差异=%s" % (
only_a, only_b, a != b and not only_a and not only_b)
def main(argv=None):
argv = list(sys.argv[1:] if argv is None else argv)
if "--check" in argv:
errs = validate()
good, msg = check_against_init()
for e in errs:
print("[PATH-ERR] %s" % e)
print("[CHECK] %s" % msg)
print("[STATS] total=%d readonly=%d write=%d" % (len(PATHS), len(READONLY_PATHS), len(WRITE_PATHS)))
return 0 if (good and not errs) else 1
print("# %s RBAC 路径清单(显式枚举,共 %d 条,无通配符)" % (MODULE_NAME, len(PATHS)))
for p in PATHS:
print(p)
errs = validate()
if errs:
for e in errs:
print("[PATH-ERR] %s" % e, file=sys.stderr)
return 1
return 0
if __name__ == "__main__":
sys.exit(main())