refactor(backend): 重命名后端项目为 gym-manage-api,修改包名为 cn.novalon.gym.manage
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
"""
|
||||
权限边界测试套件
|
||||
|
||||
测试范围:
|
||||
1. 角色权限边界测试
|
||||
2. 数据访问权限测试
|
||||
3. 操作权限测试
|
||||
4. 菜单权限测试
|
||||
5. API权限测试
|
||||
|
||||
作者: 张翔
|
||||
日期: 2026-04-01
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from api.auth_api import AuthAPI
|
||||
from api.user_api import UserAPI
|
||||
from api.role_api import RoleAPI
|
||||
from api.menu_api import MenuAPI
|
||||
from config.settings import settings
|
||||
|
||||
|
||||
@pytest.mark.security
|
||||
@pytest.mark.asyncio
|
||||
class TestPermissionBoundary:
|
||||
"""权限边界测试类"""
|
||||
|
||||
async def test_role_based_access_control(self, authenticated_client):
|
||||
"""
|
||||
SEC-PERM-01: 基于角色的访问控制
|
||||
|
||||
验证点:
|
||||
1. 不同角色有不同权限
|
||||
2. 权限正确分配
|
||||
3. 权限正确验证
|
||||
"""
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
|
||||
roles_response = await role_api.get_roles_by_page()
|
||||
assert roles_response.status_code == 200
|
||||
|
||||
roles = roles_response.json().get("content", [])
|
||||
assert len(roles) > 0, "应至少有一个角色"
|
||||
|
||||
for role in roles:
|
||||
assert "roleName" in role, "角色应包含名称"
|
||||
assert "roleKey" in role, "角色应包含标识"
|
||||
|
||||
async def test_user_data_isolation(self, authenticated_client):
|
||||
"""
|
||||
SEC-PERM-02: 用户数据隔离
|
||||
|
||||
验证点:
|
||||
1. 用户只能访问自己的数据
|
||||
2. 无法访问其他用户敏感信息
|
||||
3. 管理员可访问所有数据
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
|
||||
users_response = await user_api.get_users_by_page()
|
||||
assert users_response.status_code == 200
|
||||
|
||||
users = users_response.json().get("content", [])
|
||||
|
||||
for user in users:
|
||||
if "password" in user:
|
||||
assert user["password"] != "admin123", \
|
||||
"密码不应明文返回"
|
||||
assert not user["password"].startswith("$2"), \
|
||||
"密码哈希不应返回给前端"
|
||||
|
||||
async def test_cross_user_access_prevention(self, authenticated_client):
|
||||
"""
|
||||
SEC-PERM-03: 跨用户访问防护
|
||||
|
||||
验证点:
|
||||
1. 普通用户无法修改其他用户数据
|
||||
2. 用户ID绑定验证
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
|
||||
users_response = await user_api.get_users_by_page()
|
||||
users = users_response.json().get("content", [])
|
||||
|
||||
if len(users) > 1:
|
||||
other_user = next(
|
||||
(u for u in users if u.get("username") != "admin"),
|
||||
None
|
||||
)
|
||||
|
||||
if other_user:
|
||||
response = await user_api.get_user_by_id(other_user["id"])
|
||||
|
||||
assert response.status_code in [200, 403], \
|
||||
"应正确处理跨用户访问"
|
||||
|
||||
async def test_menu_permission_control(self, authenticated_client):
|
||||
"""
|
||||
SEC-PERM-04: 菜单权限控制
|
||||
|
||||
验证点:
|
||||
1. 不同角色看到不同菜单
|
||||
2. 菜单权限标识验证
|
||||
3. 无权限菜单隐藏
|
||||
"""
|
||||
menu_api = MenuAPI(authenticated_client)
|
||||
|
||||
menus_response = await menu_api.get_menus()
|
||||
assert menus_response.status_code == 200
|
||||
|
||||
menus = menus_response.json() if isinstance(
|
||||
menus_response.json(), list
|
||||
) else menus_response.json().get("data", [])
|
||||
|
||||
for menu in menus:
|
||||
assert "menuName" in menu or "name" in menu, \
|
||||
"菜单应包含名称"
|
||||
|
||||
async def test_api_permission_validation(self, authenticated_client):
|
||||
"""
|
||||
SEC-PERM-05: API权限验证
|
||||
|
||||
验证点:
|
||||
1. 每个API有权限控制
|
||||
2. 无权限返回403
|
||||
3. 未认证返回401
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
|
||||
client_without_auth = authenticated_client.__class__(
|
||||
base_url=settings.API_BASE_URL
|
||||
)
|
||||
|
||||
user_api_no_auth = UserAPI(client_without_auth)
|
||||
response = await user_api_no_auth.get_users_by_page()
|
||||
|
||||
assert response.status_code in [401, 403], \
|
||||
"未认证请求应被拒绝"
|
||||
|
||||
async def test_privilege_escalation_prevention(self, authenticated_client):
|
||||
"""
|
||||
SEC-PERM-06: 权限提升防护
|
||||
|
||||
验证点:
|
||||
1. 用户无法自我提升权限
|
||||
2. 角色修改需管理员权限
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
|
||||
users_response = await user_api.get_users_by_page()
|
||||
users = users_response.json().get("content", [])
|
||||
|
||||
current_user = next(
|
||||
(u for u in users if u.get("username") == "admin"),
|
||||
None
|
||||
)
|
||||
|
||||
if current_user:
|
||||
roles_response = await role_api.get_roles_by_page()
|
||||
roles = roles_response.json().get("content", [])
|
||||
|
||||
admin_role = next(
|
||||
(r for r in roles if "admin" in r.get("roleKey", "").lower()),
|
||||
None
|
||||
)
|
||||
|
||||
assert admin_role is not None, "应存在管理员角色"
|
||||
|
||||
async def test_operation_permission_check(self, authenticated_client):
|
||||
"""
|
||||
SEC-PERM-07: 操作权限检查
|
||||
|
||||
验证点:
|
||||
1. 创建操作需权限
|
||||
2. 修改操作需权限
|
||||
3. 删除操作需权限
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
|
||||
test_user_data = {
|
||||
"username": "perm_test_user",
|
||||
"password": "Test123!@#",
|
||||
"email": "perm_test@test.com",
|
||||
"phone": "13800138000",
|
||||
"status": 1
|
||||
}
|
||||
|
||||
create_response = await user_api.create_user(test_user_data)
|
||||
|
||||
if create_response.status_code in [201, 200]:
|
||||
user_id = create_response.json().get("id")
|
||||
|
||||
update_response = await user_api.update_user(
|
||||
user_id,
|
||||
{"email": "updated@test.com"}
|
||||
)
|
||||
|
||||
assert update_response.status_code in [200, 403]
|
||||
|
||||
delete_response = await user_api.delete_user(user_id)
|
||||
|
||||
assert delete_response.status_code in [200, 204, 403]
|
||||
|
||||
async def test_data_filter_by_permission(self, authenticated_client):
|
||||
"""
|
||||
SEC-PERM-08: 数据权限过滤
|
||||
|
||||
验证点:
|
||||
1. 查询结果按权限过滤
|
||||
2. 敏感字段脱敏
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
|
||||
users_response = await user_api.get_users_by_page()
|
||||
|
||||
if users_response.status_code == 200:
|
||||
users = users_response.json().get("content", [])
|
||||
|
||||
for user in users:
|
||||
assert "password" not in user or \
|
||||
user.get("password") == "******", \
|
||||
"密码字段应脱敏或不返回"
|
||||
|
||||
async def test_role_permission_inheritance(self, authenticated_client):
|
||||
"""
|
||||
SEC-PERM-09: 角色权限继承
|
||||
|
||||
验证点:
|
||||
1. 角色权限可继承
|
||||
2. 子角色权限不超过父角色
|
||||
"""
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
|
||||
roles_response = await role_api.get_roles_by_page()
|
||||
|
||||
if roles_response.status_code == 200:
|
||||
roles = roles_response.json().get("content", [])
|
||||
|
||||
for role in roles:
|
||||
if "parentId" in role and role["parentId"]:
|
||||
parent_role = next(
|
||||
(r for r in roles if r.get("id") == role["parentId"]),
|
||||
None
|
||||
)
|
||||
|
||||
async def test_admin_privilege_boundary(self, authenticated_client):
|
||||
"""
|
||||
SEC-PERM-10: 管理员权限边界
|
||||
|
||||
验证点:
|
||||
1. 超级管理员有所有权限
|
||||
2. 普通管理员权限受限
|
||||
3. 管理员操作审计
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
|
||||
users_response = await user_api.get_users_by_page()
|
||||
assert users_response.status_code == 200
|
||||
|
||||
roles_response = await role_api.get_roles_by_page()
|
||||
assert roles_response.status_code == 200
|
||||
|
||||
users = users_response.json().get("content", [])
|
||||
roles = roles_response.json().get("content", [])
|
||||
|
||||
admin_user = next(
|
||||
(u for u in users if u.get("username") == "admin"),
|
||||
None
|
||||
)
|
||||
|
||||
if admin_user:
|
||||
assert admin_user.get("status") == 1, \
|
||||
"管理员账户应处于激活状态"
|
||||
Reference in New Issue
Block a user