pbl_domain_ext/scripts/load_path.py
2026-09-18 12:12:52 +08:00

117 lines
5.3 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.

"""scripts/load_path.py — pbl_domain_extM8RBAC 路径注册。
规则module-development-spec新增契约必须在此显式注册 .dspy/.ui 路径,
**禁止通配符**'*' / 前缀模糊匹配一律不允许),否则新端点上线即 403。
路径与 wwwroot/ 下实际文件一一对应13 个契约 dspy + 1 个 index.ui
"""
MODULE_NAME = 'pbl_domain_ext'
# 契约端点(设计 modules/pbl_domain_ext.md §3 的 13 个接口,一一对应)
API_PATHS = [
# §3.1 关联管理5
'/pbl_domain_ext/api/pbl_domain_ref_bind.dspy', # bind_ref
'/pbl_domain_ext/api/pbl_domain_ref_unbind.dspy', # unbind_ref
'/pbl_domain_ext/api/pbl_domain_ref_get.dspy', # get_ref
'/pbl_domain_ext/api/pbl_domain_ref_list.dspy', # list_refs
'/pbl_domain_ext/api/pbl_domain_ref_update.dspy', # update_ref
# §3.2 租户隔离查询封装5
'/pbl_domain_ext/api/pbl_world_list_by_tenant.dspy', # list_worlds_by_tenant
'/pbl_domain_ext/api/pbl_scene_list_by_world.dspy', # list_scenes_by_world
'/pbl_domain_ext/api/pbl_entity_list_by_scene.dspy', # list_entities_by_scene
'/pbl_domain_ext/api/pbl_world_get_context.dspy', # get_world_with_pbl_context
'/pbl_domain_ext/api/pbl_domain_ref_check_access.dspy', # check_ref_access
# §3.3 团队/班级维度3
'/pbl_domain_ext/api/pbl_team_list_by_class.dspy', # list_teams_by_class
'/pbl_domain_ext/api/pbl_team_bind_world.dspy', # bind_team_to_world
'/pbl_domain_ext/api/pbl_team_world_list.dspy', # get_team_worlds
]
# 前端页面
UI_PATHS = [
'/pbl_domain_ext/index.ui',
]
# 角色授权教师pbl_teacher可管理关联学生pbl_student只读查询 + 访问校验;
# 管理员admin全量。角色名沿用 pbl_governance / rbac 既有定义,本模块不新增角色。
ROLE_GRANTS = {
'/pbl_domain_ext/api/pbl_domain_ref_bind.dspy': ['admin', 'pbl_teacher'],
'/pbl_domain_ext/api/pbl_domain_ref_unbind.dspy': ['admin', 'pbl_teacher'],
'/pbl_domain_ext/api/pbl_domain_ref_get.dspy': ['admin', 'pbl_teacher', 'pbl_student'],
'/pbl_domain_ext/api/pbl_domain_ref_list.dspy': ['admin', 'pbl_teacher'],
'/pbl_domain_ext/api/pbl_domain_ref_update.dspy': ['admin', 'pbl_teacher'],
'/pbl_domain_ext/api/pbl_world_list_by_tenant.dspy': ['admin', 'pbl_teacher', 'pbl_student'],
'/pbl_domain_ext/api/pbl_scene_list_by_world.dspy': ['admin', 'pbl_teacher', 'pbl_student'],
'/pbl_domain_ext/api/pbl_entity_list_by_scene.dspy': ['admin', 'pbl_teacher', 'pbl_student'],
'/pbl_domain_ext/api/pbl_world_get_context.dspy': ['admin', 'pbl_teacher', 'pbl_student'],
'/pbl_domain_ext/api/pbl_domain_ref_check_access.dspy': ['admin', 'pbl_teacher', 'pbl_student'],
'/pbl_domain_ext/api/pbl_team_list_by_class.dspy': ['admin', 'pbl_teacher', 'pbl_student'],
'/pbl_domain_ext/api/pbl_team_bind_world.dspy': ['admin', 'pbl_teacher'],
'/pbl_domain_ext/api/pbl_team_world_list.dspy': ['admin', 'pbl_teacher', 'pbl_student'],
'/pbl_domain_ext/index.ui': ['admin', 'pbl_teacher', 'pbl_student'],
}
ALL_PATHS = API_PATHS + UI_PATHS
def get_paths():
"""返回本模块需注册的全部路径(供宿主 rbac 批量 load_path 使用)。"""
return list(ALL_PATHS)
def get_roles(path):
"""返回某路径的授权角色列表未注册路径返回空fail-closed不放行"""
return list(ROLE_GRANTS.get(path, []))
def register(env=None):
"""向宿主 rbac 注册路径幂等。env 缺省自动取 ServerEnv。"""
if env is None:
try:
from ahserver.serverenv import ServerEnv # type: ignore
env = ServerEnv()
except Exception:
env = None
registered = []
for path in ALL_PATHS:
roles = ROLE_GRANTS.get(path, [])
if env is not None:
for name in ('load_path', 'rbac_load_path', 'add_path', 'register_path'):
func = getattr(env, name, None)
if callable(func):
try:
func(path, roles)
except TypeError:
func(path)
break
registered.append({'path': path, 'roles': roles, 'module': MODULE_NAME})
return registered
def selfcheck():
"""自检路径无通配符、API/UI 全部有角色授权、无重复。"""
problems = []
seen = set()
for path in ALL_PATHS:
if '*' in path or '?' in path:
problems.append('通配符路径禁止注册: %s' % path)
if path in seen:
problems.append('重复注册: %s' % path)
seen.add(path)
if not ROLE_GRANTS.get(path):
problems.append('缺少角色授权: %s' % path)
if not path.startswith('/%s/' % MODULE_NAME):
problems.append('路径未落在模块前缀下: %s' % path)
return problems
if __name__ == '__main__':
issues = selfcheck()
print('module=%s paths=%d api=%d ui=%d' % (MODULE_NAME, len(ALL_PATHS),
len(API_PATHS), len(UI_PATHS)))
if issues:
for issue in issues:
print('FAIL %s' % issue)
raise SystemExit(1)
print('OK 全部路径显式注册、无通配符、角色授权齐备')