#!/usr/bin/env python3 # -*- coding: utf-8 -*- """world_sync 模块 RBAC / 路径声明脚本(per-module,双写中央 apps/pbls/scripts/load_path.py)。 用法: python3 load_path.py # 找到 Sage 根时逐条 set_role_perm 登记;找不到则打印跳过说明 python3 load_path.py --check # 离线自检:不连库,校验声明合规(无通配符、分层正确)并回显登记表 ═══════════════════════════════════════════════════════════════════════════════ 里程碑 RBAC 声明台账(每个里程碑必须显式登记一行,禁止留空/待补) ═══════════════════════════════════════════════════════════════════════════════ M11b-2(T3 本任务,2026-09-22): 结论 = 无新路径登记。 理由 = M11b-2 里程碑(world_sync 列级差异比对 + 单事务事件/状态写入 pbl_runtime_ext) 只交付 **Python 库函数**(world_sync/*.py、pbl_runtime_ext/*.py)与 models/*.json 表定义,**不新增任何 .dspy / .ui / .js / .css 前端可访问路径**, 因此本里程碑在 RBAC 中 **零新增登记项**;下方 PATHS_ANY / PATHS_LOGINED 为 M11b-2 之前既有页面/接口路径的存量声明,保持原样未增删。 铁律 = 通配符(% / *)在本脚本中一律禁止,所有路径逐条显式声明; 若后续里程碑(M11b-3 及以后)新增 .dspy/.ui/.js/.css,必须按 any / logined / owner.superuser / owner.operator 分层在本脚本 + 中央 apps/pbls/scripts/load_path.py 双写登记,并在本台账追加一行。 ═══════════════════════════════════════════════════════════════════════════════ """ import os import sys MODULE = 'world_sync' # ── 既有(M11b-2 之前)路径:any 层——无需登录即可访问的静态/公共资源 ────────── PATHS_ANY = [ f'/{MODULE}', ] # ── 既有(M11b-2 之前)路径:logined 层——.ui 页面与 .dspy 接口 ──────────────── # M11b-2 未在此列表新增任何条目(见文件头「里程碑 RBAC 声明台账」)。 PATHS_LOGINED = [ f'/{MODULE}', f'/{MODULE}/index.ui', f'/{MODULE}/sync_form.ui', f'/{MODULE}/world_sync', f'/{MODULE}/world_sync/index.ui', f'/{MODULE}/world_sync/get_world_sync.dspy', f'/{MODULE}/world_sync/add_world_sync.dspy', f'/{MODULE}/world_sync/update_world_sync.dspy', f'/{MODULE}/world_sync/delete_world_sync.dspy', f'/{MODULE}/api/world_sync.dspy', f'/{MODULE}/api/create_world_sync.dspy', f'/{MODULE}/api/world_sync_update.dspy', f'/{MODULE}/api/world_sync_delete.dspy', f'/{MODULE}/api/get_search_world_id.dspy', f'/{MODULE}/api/get_search_sync_type.dspy', f'/{MODULE}/api/get_search_sync_status.dspy', ] # ── 既有:owner.* 角色受限路径(当前无,显式声明为空,避免「漏登记」被误判为「待补」) PATHS_OWNER_SUPERUSER = [] PATHS_OWNER_OPERATOR = [] # ── 里程碑声明台账(供 --check 回显,也是 QC 可 grep 的硬证据)──────────────── MILESTONE_DECLARATIONS = [ { 'milestone': 'M11b-2', 'new_paths': 0, 'declaration': ( 'M11b-2 本里程碑不新增 .dspy/.ui/.js/.css 路径,仅提供库函数' '(world_sync / pbl_runtime_ext 的 *.py 与 models/*.json),' '故无 RBAC 登记项;禁通配符(%/*),' '如有路径须按 any/logined/owner.* 分层逐条登记。' ), }, ] FORBIDDEN_WILDCARDS = ('%', '*') def _find_sage_root(): for c in [os.path.expanduser('~/repos/sage'), os.path.expanduser('~/sage'), '/d/ymq/repos/sage']: if os.path.isdir(os.path.join(c, 'wwwroot')) and os.path.isdir(os.path.join(c, 'py3', 'bin')): return c return None def _assert_no_wildcard(): """通配符硬门禁:任何一层路径出现 % 或 * 直接失败退出(非静默)。""" bad = [] for tier, paths in ( ('any', PATHS_ANY), ('logined', PATHS_LOGINED), ('owner.superuser', PATHS_OWNER_SUPERUSER), ('owner.operator', PATHS_OWNER_OPERATOR), ): for p in paths: if any(w in p for w in FORBIDDEN_WILDCARDS): bad.append(f'{tier}:{p}') if bad: print(f'[world_sync] FAIL 路径声明含禁用通配符: {bad}') return 1 print('[world_sync] OK 通配符检查通过(PATHS_ANY/LOGINED/OWNER.* 均为逐条显式路径,无 % / *)') return 0 def check(): """离线自检模式:不连库、不写权限表,只回显声明台账与合规检查结果。""" print('=' * 78) print(f'[world_sync] RBAC 路径声明自检 (--check) module={MODULE}') print('=' * 78) rc = _assert_no_wildcard() print('\n[声明台账]') for d in MILESTONE_DECLARATIONS: print(f" - {d['milestone']}: 新增路径 {d['new_paths']} 条 -> {d['declaration']}") print('\n[无新 API 路径正式声明]') print(' M11b-2(含 T1/T2/T3 子任务)未新增任何 .dspy / .ui / .js / .css 可访问路径;') print(' 本里程碑交付物为库函数(world_sync/*.py、pbl_runtime_ext/*.py)与表定义') print(' (models/world_sync_column_map.json 等),由宿主应用进程内调用,不经 HTTP 路由,') print(' 因此本里程碑在 RBAC permission 表中零新增条目。') print(' 说明文件: modules/world_sync/wwwroot/api/README-N-A-M11b2.md') print(' 中央双写: apps/pbls/scripts/load_path.py 含等价 M11b-2 声明。') print('\n[存量路径登记(M11b-2 之前既有,未增删)]') for tier, paths in ( ('any', PATHS_ANY), ('logined', PATHS_LOGINED), ('owner.superuser', PATHS_OWNER_SUPERUSER), ('owner.operator', PATHS_OWNER_OPERATOR), ): print(f' tier={tier} count={len(paths)}') for p in paths: print(f' {p}') total = len(PATHS_ANY) + len(PATHS_LOGINED) + len(PATHS_OWNER_SUPERUSER) + len(PATHS_OWNER_OPERATOR) print(f'\n[汇总] 存量路径合计 {total} 条;M11b-2 新增 0 条') print('[world_sync] --check 结论: PASS(无新路径需登记,声明已显式化)' if rc == 0 else '[world_sync] --check 结论: FAIL(存在违规通配符)') return rc def main(): if '--check' in sys.argv[1:]: return check() root = _find_sage_root() if not root: print('[world_sync] 未找到 Sage 根目录,跳过(请确认中央 load_path.py 已登记)') print('[world_sync] 本里程碑 M11b-2 无新路径登记(仅库函数),跳过不影响 RBAC 完整性;') print('[world_sync] 可执行 `python3 load_path.py --check` 离线校验声明合规性。') return 0 sys.path.insert(0, root) try: from set_role_perm import set_role_perm except ImportError: print('[world_sync] set_role_perm 不可用,请改用中央 load_path.py') return 0 rc = _assert_no_wildcard() if rc != 0: return rc for p in PATHS_ANY: set_role_perm(p, 'any') for p in PATHS_LOGINED: set_role_perm(p, 'logined') for p in PATHS_OWNER_SUPERUSER: set_role_perm(p, 'owner.superuser') for p in PATHS_OWNER_OPERATOR: set_role_perm(p, 'owner.operator') print(f'[world_sync] 已注册 {len(PATHS_ANY)+len(PATHS_LOGINED)+len(PATHS_OWNER_SUPERUSER)+len(PATHS_OWNER_OPERATOR)} 条 RBAC 路径' f'(M11b-2 新增 0 条,全部为存量路径)') return 0 if __name__ == '__main__': sys.exit(main())