From 411d1017cf86e75d4f5c4c14da54c11806d213f1 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Thu, 27 Aug 2026 18:17:45 +0800 Subject: [PATCH] =?UTF-8?q?fix(load=5Fpath):=20review=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E2=80=94=E2=80=94=E5=8D=95=E4=BA=8B=E4=BB=B6=E5=BE=AA=E7=8E=AF?= =?UTF-8?q?=E7=9B=B4=E8=BF=9E=E6=B3=A8=E5=86=8C(=E5=8E=BB=E6=8E=89600+?= =?UTF-8?q?=E6=AC=A1subprocess)=EF=BC=8Cget=5Faccdetail=E6=81=A2=E5=A4=8Dp?= =?UTF-8?q?age/sort?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- accounting/init.py | 4 +- scripts/load_path.py | 168 +++++++++++++++++++++---------------------- 2 files changed, 84 insertions(+), 88 deletions(-) diff --git a/accounting/init.py b/accounting/init.py index f16d185..18470b7 100644 --- a/accounting/init.py +++ b/accounting/init.py @@ -48,7 +48,9 @@ where b.subjectid = c.id """ ns = { 'accountid': accountid, - 'orgid': userorgid + 'orgid': userorgid, + 'page': page, + 'sort': 'acc_date desc' } ret = await sor.sqlExe(sql, ns) return ret diff --git a/scripts/load_path.py b/scripts/load_path.py index 1c45d4e..613b679 100644 --- a/scripts/load_path.py +++ b/scripts/load_path.py @@ -22,11 +22,10 @@ accounting 模块 RBAC 权限管理脚本(角色分层版,2026-08-27 重构 用 orgtypeid+name 查 role 表解析出真实 role.id 后注册,同名多条全部注册。 使用方法: - cd (含 py3/ 与 set_role_perm.py 的目录,如 /d/pipeline/pipeline-app) + cd (含 py3/ 的目录,如 /d/pipeline/pipeline-app) ./py3/bin/python pkgs/accounting/scripts/load_path.py [--add-only] """ -import subprocess import os import sys import asyncio @@ -51,9 +50,6 @@ if not APP_ROOT: print("ERROR: Cannot find app root directory") sys.exit(1) -PYTHON = os.path.join(APP_ROOT, "py3", "bin", "python") -SET_PERM_SCRIPT = os.path.join(APP_ROOT, "set_role_perm.py") - MOD = "accounting" # ============================================================ @@ -252,102 +248,100 @@ async def _find_rbac_db(db): return None -async def resolve_role_ids(labels): +async def resolve_role_ids(sor, labels): """标签 'orgtypeid.name' → 真实 role.id 列表(同名多条全返回)。""" - db = _get_db() - dbname = await _find_rbac_db(db) mapping = {} - if not dbname: - print('WARN: role 表不可达,按字面量注册(可能无效)') - return {lb: [lb] for lb in labels} - async with db.sqlorContext(dbname) as sor: - for label in labels: - if label in ('any', 'logined', 'anonymous'): - mapping[label] = [label] - continue - orgtypeid, _, name = label.partition('.') - recs = await sor.sqlExe( - 'select id from role where orgtypeid=${o}$ and name=${n}$', - {'o': orgtypeid, 'n': name}) - ids = [_row_get(r, 'id') for r in recs] - ids = [i for i in ids if i] - if not ids: - print(f'WARN: 角色不存在 {label},跳过') - mapping[label] = ids - await sor.sqlExe('COMMIT', {}) + for label in labels: + if label in ('any', 'logined', 'anonymous'): + mapping[label] = [label] + continue + orgtypeid, _, name = label.partition('.') + recs = await sor.sqlExe( + 'select id from role where orgtypeid=${o}$ and name=${n}$', + {'o': orgtypeid, 'n': name}) + ids = [_row_get(r, 'id') for r in recs] + ids = [i for i in ids if i] + if not ids: + print(f'WARN: 角色不存在 {label},跳过') + mapping[label] = ids return mapping -async def clean_accounting_roleperms(): - """删除 /accounting/ 下全部旧授权(保留 permission 行本身)。""" - db = _get_db() - dbname = await _find_rbac_db(db) - if not dbname: - print('ERROR: 无法定位 RBAC 库,清理中止') - return -1 - total = 0 - async with db.sqlorContext(dbname) as sor: - perms = await sor.sqlExe( - 'select id from permission where path like ${p}$ or path=${p2}$', - {'p': f'/{MOD}/%', 'p2': f'/{MOD}'}) - for p in perms: - pid = _row_get(p, 'id') - r = await sor.sqlExe('delete from rolepermission where permid=${pid}$', {'pid': pid}) - total += 1 - await sor.sqlExe('COMMIT', {}) - print(f'清理完成:/accounting/ 下 {len(perms)} 个 permission 的旧授权已删除') - return total - - -# ============================================================ -# 注册 -# ============================================================ - -def run_set_perm(role_id, path): - cmd = [PYTHON, SET_PERM_SCRIPT, role_id, path] - result = subprocess.run(cmd, capture_output=True, text=True, cwd=APP_ROOT) - if result.returncode != 0: - print(f" FAIL {role_id} {path}: {(result.stderr or result.stdout).strip()[:200]}") +async def set_role_perm(sor, role_id, path): + """注册单条权限(幂等):permission 缺则建,rolepermission 缺则建。""" + from appPublic.uniqueID import getID + recs = await sor.R('permission', {'path': path}) + if not recs: + permid = getID() + await sor.C('permission', {'id': permid, 'path': path}) + else: + permid = _row_get(recs[0], 'id') + rp = await sor.R('rolepermission', {'roleid': role_id, 'permid': permid}) + if rp: return False + await sor.C('rolepermission', {'id': getID(), 'roleid': role_id, 'permid': permid}) return True -def register_role_paths(role_label, role_ids, paths): - if not role_ids: - return 0 - count = 0 - for rid in role_ids: - for p in paths: - if run_set_perm(rid, p): - count += 1 - print(f" {role_label} ({','.join(role_ids)}): {count}/{len(paths) * len(role_ids)} entries registered") - return count +async def run_all(add_only): + db = _get_db() + dbname = await _find_rbac_db(db) + if not dbname: + print('ERROR: 无法定位 RBAC 库(role 表不可达),中止') + return 1 + print(f'RBAC 库: {dbname}') + + total = 0 + async with db.sqlorContext(dbname) as sor: + if not add_only: + print('=== 清理旧授权 ===') + perms = await sor.sqlExe( + 'select id from permission where path like ${p}$ or path=${p2}$', + {'p': f'/{MOD}/%', 'p2': f'/{MOD}'}) + for p in perms: + await sor.sqlExe('delete from rolepermission where permid=${pid}$', + {'pid': _row_get(p, 'id')}) + await sor.sqlExe('COMMIT', {}) + print(f'清理完成:/accounting/ 下 {len(perms)} 个 permission 的旧授权已删除') + + print('=== 解析角色 ===') + all_labels = sorted(set(FIN_ROLES + PROXY_ROLES)) + role_map = await resolve_role_ids(sor, all_labels) + for lb in all_labels: + print(f' {lb} -> {role_map.get(lb) or "(不存在)"}') + + print('=== 注册新矩阵 ===') + + async def register_group(group_label, role_ids, paths): + nonlocal total + if not role_ids: + return + added = 0 + for rid in role_ids: + for p in paths: + if await set_role_perm(sor, rid, p): + added += 1 + await sor.sqlExe('COMMIT', {}) + total += added + print(f' {group_label} ({",".join(role_ids)}): +{added} 条' + f'(目标 {len(paths) * len(role_ids)} 条,其余为已存在)') + + await register_group('any', ['any'], PATHS_ANY) + await register_group('logined', ['logined'], PATHS_LOGINED) + for label in FIN_ROLES: + await register_group(label, role_map.get(label, []), PATHS_FIN) + for label in PROXY_ROLES: + await register_group(label, role_map.get(label, []), PATHS_PROXY) + + print(f'\nDone. 本次新增 {total} 条授权。') + print('NOTE: 重启应用(或调用 /rbac/refresh_userperm.dspy)后权限生效。') + return 0 def main(): add_only = '--add-only' in sys.argv print(f"App root: {APP_ROOT}") - - if not add_only: - print('=== 清理旧授权 ===') - asyncio.run(clean_accounting_roleperms()) - - print('=== 解析角色 ===') - all_labels = sorted(set(FIN_ROLES + PROXY_ROLES)) - role_map = asyncio.run(resolve_role_ids(all_labels)) - for lb in all_labels: - print(f' {lb} -> {role_map.get(lb) or "(不存在)"}') - - print('=== 注册新矩阵 ===') - total = 0 - total += register_role_paths('any', ['any'], PATHS_ANY) - total += register_role_paths('logined', ['logined'], PATHS_LOGINED) - for label in FIN_ROLES: - total += register_role_paths(label, role_map.get(label, []), PATHS_FIN) - for label in PROXY_ROLES: - total += register_role_paths(label, role_map.get(label, []), PATHS_PROXY) - print(f"\nDone. Total {total} permission entries registered.") - print("NOTE: 重启应用(或调用 /rbac/refresh_userperm.dspy)后权限生效。") + sys.exit(asyncio.run(run_all(add_only))) if __name__ == "__main__":