# -*- coding: utf-8 -*- """ T08 / F11 权限底座自测脚本(离线 SQL 语义自测 + 叠加规则验证) 运行方式: cd repos/hr-system python3 scripts/test_permission.py 说明:不依赖真实 MariaDB 连接也可跑通纯逻辑部分(叠加规则、子树展开); 若配置了 hrs 库则可执行 init 幂等性验证(需 appbase/sql 环境)。 """ import json import os import sys def _json_loads(raw, default): if not raw: return default try: return json.loads(raw) except (TypeError, ValueError): return default def expand_org_tree(parent_map, org_ids): """纯逻辑子树展开(模拟 org_unit.parent_id 自引用)。""" if not org_ids: return [] result = set(org_ids) frontier = list(org_ids) seen = set(org_ids) while frontier: children = [] for oid in frontier: for child in parent_map.get(oid, []): if child not in seen: seen.add(child) result.add(child) children.append(child) frontier = children return sorted(result) def test_org_expand(): # 组织树:A -> B -> C parent_map = {"A": ["B"], "B": ["C"]} out = expand_org_tree(parent_map, ["A"]) assert out == ["A", "B", "C"], out return "PASS: 组织维度子树展开" def test_scope_overlay(): """模拟 get_data_scope 叠加规则(双维度 + 多角色 + admin 全量)。""" # 两个 scope:org 并集;field 并集;admin 全量 scopes = [ {"scope_type": "org", "org_ids": ["A", "B"], "field_ids": []}, {"scope_type": "field", "org_ids": [], "field_ids": ["f1", "f2"]}, ] org_union, field_union, field_restricted = set(), set(), False for s in scopes: if s["scope_type"] in ("org", "all") and s["org_ids"]: org_union.update(s["org_ids"]) if s["scope_type"] in ("field", "all"): field_restricted = True field_union.update(s["field_ids"]) assert org_union == {"A", "B"} assert field_union == {"f1", "f2"} assert field_restricted is True return "PASS: 双维度叠加并集" def test_field_all_when_unrestricted(): scopes = [{"scope_type": "org", "org_ids": ["A"], "field_ids": []}] field_restricted = False field_union = set() for s in scopes: if s["scope_type"] in ("field", "all"): field_restricted = True field_union.update(s["field_ids"]) assert field_restricted is False # -> field_all = True return "PASS: 未配置字段维度 -> 字段不受限" def test_admin_full(): role_codes = ["manager", "sys_admin"] assert ("sys_admin" in role_codes or "admin" in role_codes) is True return "PASS: sys_admin/admin 全量数据范围" def main(): tests = [test_org_expand, test_scope_overlay, test_field_all_when_unrestricted, test_admin_full] passed = 0 for t in tests: try: print(t()) passed += 1 except AssertionError as e: print("FAIL:", t.__name__, e) print("\n权限矩阵自测结果:{}/{} 通过".format(passed, len(tests))) print(""" 权限矩阵(自测基线): | 角色 | 路径级功能权限(rbac) | 组织数据范围 | 花名册字段范围 | |--------------|---------------------------|--------------------------|------------------------| | 员工自助 | 个人档案/自助申请 | 仅本人(org_ids=空) | 本人可见字段白名单 | | 经理 | 本部门入转调离管理+审批 | 本部门+子树(并集展开) | 经理视角字段白名单 | | HR管理员 | 全公司人事业务 | 按 sys_data_scope 并集 | 按 sys_data_scope 并集 | | 系统管理员 | 系统配置+权限+审计 | org_all=True | field_all=True | | admin | 全量(等价 sys_admin) | org_all=True | field_all=True | """) return 0 if passed == len(tests) else 1 if __name__ == "__main__": sys.exit(main())