165 lines
7.9 KiB
Python
165 lines
7.9 KiB
Python
#!/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())
|