# -*- coding: utf-8 -*- """RBAC 端到端测试:角色 / 权限 / 数据范围 / 审计。""" from __future__ import annotations from fastapi.testclient import TestClient def login(client: TestClient, username: str, password: str = "123456") -> str: res = client.post("/auth/login", json={"username": username, "password": password}) assert res.status_code == 200, res.text return res.json()["token"] def auth(token: str) -> dict: return {"Authorization": f"Bearer {token}"} # --------------------------------------------------------------------------- # 角色访问控制 # --------------------------------------------------------------------------- def test_operator_can_list_users_enterprise_cannot(client): op = login(client, "pine") # operator / op_super_admin ent = login(client, "ent01") # enterprise assert client.get("/admin/users", headers=auth(op)).status_code == 200 assert client.get("/admin/users", headers=auth(ent)).status_code == 403 def test_assign_role_requires_permission(client): # op_analyst 有 operator 角色但缺 action:user.assign_role analyst = login(client, "op_analyst") res = client.post( "/admin/users/u_ent_01/role", headers=auth(analyst), json={"role": "carrier"}, ) assert res.status_code == 403 def test_super_admin_can_assign_role(client): op = login(client, "pine") res = client.post( "/admin/users/u_ent_01/role", headers=auth(op), json={"role": "carrier", "org_id": "o_car_001", "region_id": "r_dist_wh"}, ) assert res.status_code == 200, res.text body = res.json() assert body["user"]["role"] == "carrier" # --------------------------------------------------------------------------- # 数据范围层级(上级可看下级,下级不可看上级) # --------------------------------------------------------------------------- def test_government_province_sees_all_regions(client): prov = login(client, "gov_prov") regions = client.get("/government/regions", headers=auth(prov)).json() ids = {r["id"] for r in regions} assert "r_prov_yn" in ids and "r_dist_wh" in ids and "r_dist_ql" in ids def test_government_district_sees_only_own_region(client): dist = login(client, "gov_dist") # r_dist_wh regions = client.get("/government/regions", headers=auth(dist)).json() ids = {r["id"] for r in regions} assert ids == {"r_dist_wh"} def test_district_gov_cannot_access_out_of_scope_enterprise(client): dist = login(client, "gov_dist") # 只覆盖 r_dist_wh # o_ent_prov 在 r_prov_yn,超出区县范围 res = client.get("/government/enterprises/o_ent_prov", headers=auth(dist)) assert res.status_code == 403 def test_province_gov_can_access_any_enterprise(client): prov = login(client, "gov_prov") res = client.get("/government/enterprises/o_ent_prov", headers=auth(prov)) assert res.status_code == 200 # --------------------------------------------------------------------------- # 禁用用户 / 审计 # --------------------------------------------------------------------------- def test_disabled_user_token_rejected(client): op = login(client, "pine") ent = login(client, "ent01") # 禁用前取得的 token # 禁用 ent01 res = client.post( "/admin/users/u_ent_01/status", headers=auth(op), json={"status": "disabled"}, ) assert res.status_code == 200 # 已登录的 ent01 token 立即失效 assert client.get("/auth/verify", headers=auth(ent)).status_code == 401 # 重新登录被拒(禁用) assert ( client.post( "/auth/login", json={"username": "ent01", "password": "123456"}, ).status_code == 403 ) def test_role_change_and_data_view_write_audit(client): op = login(client, "pine") prov = login(client, "gov_prov") client.post( "/admin/users/u_ent_01/role", headers=auth(op), json={"role": "provider"}, ) client.get("/government/enterprises", headers=auth(prov)) logs = client.get("/admin/audit-logs", headers=auth(op)).json() actions = {l["action"] for l in logs} assert "role.assign" in actions assert "data.view" in actions