pbl_scense_ext/scripts/load_path.py
2026-09-21 12:20:49 +08:00

112 lines
4.8 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.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""pbl_scense_ext RBAC 路径注册(硬门禁 6.6 / QC #11 / M9 重做 QC #1)。
约定:
- 路径 = 模块自动路由 `/pbl_scense_ext/...`,不带端口、不带 /wss 前缀;
- 角色 `any` = 免登录可取(覆盖层引导脚本 loader.js、覆盖层主题 CSS——须在
鉴权完成前即可注入,否则预鉴权页壳拿不到 loader);
- 角色 `logined` = 登录后可访问(契约 .dspy 接口、覆盖层业务 JS、12 个页面壳 .ui);
- 禁止 `%` / `*` 通配符(module-development-spec「load_path.py Wildcards are FORBIDDEN」),
每个文件一条显式条目;新增/删除静态资源必须同步本表;
- 由 apps/pbls/build.sh 第 8 步调用 `register()`;rbac CLI 不在位时打印清单(不静默跳过)。
"""
import os
import subprocess
import sys
MODULE = 'pbl_scense_ext'
# 覆盖层 12 页面壳(wwwroot/pbl_overlay/*.ui)——与 wwwroot/overlay/pbl_overlay_core.js
# PAGES 注册表 route 同源(route '/pbl_overlay/<key>.ui' → 本模块目录 pbl_overlay/<key>.ui)
OVERLAY_PAGES = [
'home', 'blueprint_list', 'blueprint_editor', 'validation_report',
'agent_console', 'approval_center', 'play_preview', 'mission_board',
'role_panel', 'artifact_studio', 'evidence_wall', 'assessment_report',
]
# (path, role)
PATHS = [
# ── 契约接口(4 个 .dspy,均需登录) ──
('/pbl_scense_ext/api/pbl_session_context_get.dspy', 'logined'),
('/pbl_scense_ext/api/pbl_page_registry_list.dspy', 'logined'),
('/pbl_scense_ext/api/pbl_playtest_feedback_save.dspy', 'logined'),
('/pbl_scense_ext/api/pbl_overlay_bootstrap.dspy', 'logined'),
# ── 模块入口页 ──
('/pbl_scense_ext', 'logined'),
('/pbl_scense_ext/index.ui', 'logined'),
# ── 覆盖层静态资源(QC #1 硬门禁:ahserver 自动托管 .css/.js 但仍受 RBAC 约束,
# 未注册即 403,M9 前端全废) ──
# loader.js 是页壳引导注入器,须在预鉴权页壳里就能加载 → any
('/pbl_scense_ext/overlay/pbl_overlay_loader.js', 'any'),
# 主题 CSS 同理(页壳首屏样式,不含业务数据)→ any
('/pbl_scense_ext/overlay/pbl_overlay.css', 'any'),
# 业务 JS 只在登录后由 loader 注入 → logined
('/pbl_scense_ext/overlay/pbl_overlay_core.js', 'logined'),
('/pbl_scense_ext/overlay/pbl_overlay_transport.js', 'logined'),
('/pbl_scense_ext/overlay/pbl_overlay_play.js', 'logined'),
]
# 覆盖层页面壳 .ui:目录 + 每个文件显式注册(登录后可见,含世界内覆盖层页)
for _key in OVERLAY_PAGES:
PATHS.append(('/pbl_scense_ext/pbl_overlay/%s.ui' % _key, 'logined'))
PATHS.append(('/pbl_scense_ext/pbl_overlay', 'logined'))
def register():
tool = os.environ.get('RBAC_SET_PERM', 'set_role_perm.py')
done, missing = 0, []
for path, role in PATHS:
if subprocess.call([sys.executable if os.environ.get('PY') else 'python3',
tool, role, path],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0:
done += 1
else:
missing.append((path, role))
print('[%s] rbac paths: total=%d ok=%d pending=%d' % (MODULE, len(PATHS), done, len(missing)))
for path, role in missing:
print(' PENDING %-12s %s' % (role, path))
return len(missing) == 0
def audit():
"""自检:PATHS 中的 overlay/pbl_overlay 条目与磁盘文件一一对应(无悬空、无漏注册)。"""
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
problems = []
# 1) 磁盘上有但未注册
for sub in ('overlay', 'pbl_overlay'):
d = os.path.join(root, 'wwwroot', sub)
if not os.path.isdir(d):
continue
for fn in sorted(os.listdir(d)):
if not fn.endswith(('.js', '.css', '.ui')):
continue
p = '/%s/%s/%s' % (MODULE, sub, fn)
if p not in [x[0] for x in PATHS]:
problems.append('UNREGISTERED %s' % p)
# 2) 注册了但磁盘缺文件(页面壳条目)
have = set('/%s/pbl_overlay/%s.ui' % (MODULE, k) for k in OVERLAY_PAGES)
for path, _role in PATHS:
if '/pbl_overlay/' in path and path not in have:
problems.append('BAD ENTRY %s' % path)
for path in sorted(have):
f = os.path.join(root, 'wwwroot', 'pbl_overlay', os.path.basename(path))
if not os.path.isfile(f):
problems.append('MISSING FILE %s' % path)
# 3) 通配符零容忍
for path, _role in PATHS:
if '%' in path or '*' in path:
problems.append('WILDCARD FORBIDDEN %s' % path)
return problems
if __name__ == '__main__':
bad = audit()
for b in bad:
print('AUDIT %s' % b)
if '--audit' in sys.argv:
sys.exit(1 if bad else 0)
sys.exit(0 if register() else 1)