统一项目权限读取模型

1、合并列表与详情读取权限,统一使用 subjects:read、project_members:read、monitoring_issues:read 等业务读取 key。

2、增加历史读取权限 key 的兼容映射,确保存量项目权限覆盖值不会丢失。

3、同步前端权限工具、路由权限与权限管理页面,避免继续引用已合并的旧权限 key。

4、补充项目角色启停保护与抽屉未保存变更守卫,防止停用 PM 或仍有关联成员的角色。
This commit is contained in:
Cheng Zhou
2026-06-04 11:06:03 +08:00
parent 44d69c2d7b
commit ab4f0d93ed
22 changed files with 852 additions and 349 deletions
@@ -62,7 +62,8 @@ async def test_replace_api_endpoint_permissions_single_role(db_session: AsyncSes
assert isinstance(result, dict)
assert "CRA" in result
assert result["CRA"]["subjects:create"]["allowed"] is True
assert result["CRA"]["subjects:list"]["allowed"] is True
assert "subjects:list" not in result["CRA"]
assert result["CRA"]["subjects:read"]["allowed"] is True
assert result["CRA"]["subjects:update"]["allowed"] is True
@@ -85,11 +86,35 @@ async def test_replace_api_endpoint_permissions_multiple_roles(db_session: Async
result = await replace_api_endpoint_permissions(db_session, study_id, payload)
assert result["CRA"]["subjects:create"]["allowed"] is True
assert result["CRA"]["subjects:list"]["allowed"] is True
assert result["PV"]["subjects:list"]["allowed"] is True
assert "subjects:list" not in result["CRA"]
assert result["CRA"]["subjects:read"]["allowed"] is True
assert "subjects:list" not in result["PV"]
assert result["PV"]["subjects:read"]["allowed"] is True
@pytest.mark.asyncio
async def test_replace_api_endpoint_permissions_accepts_legacy_read_alias_denials(db_session: AsyncSession):
"""历史列表读取 key 写入时应归并到当前读取权限,拒绝值不能被默认权限吞掉。"""
study_id = uuid.uuid4()
result = await replace_api_endpoint_permissions(
db_session,
study_id,
{
"CRA": {
"subjects:list": False,
}
},
)
assert "subjects:list" not in result["CRA"]
assert result["CRA"]["subjects:read"]["allowed"] is False
stored = await get_api_endpoint_permissions(db_session, study_id)
assert "subjects:list" not in stored["CRA"]
assert stored["CRA"]["subjects:read"]["allowed"] is False
@pytest.mark.asyncio
async def test_replace_api_endpoint_permissions_deny(db_session: AsyncSession):
"""测试替换权限为拒绝"""
@@ -152,11 +177,11 @@ async def test_replace_api_endpoint_permissions_multiple_endpoints(db_session: A
result = await replace_api_endpoint_permissions(db_session, study_id, payload)
assert result["CRA"]["subjects:create"]["allowed"] is True
assert result["CRA"]["subjects:list"]["allowed"] is True
assert result["CRA"]["subjects:read"]["allowed"] is True
assert result["CRA"]["subjects:update"]["allowed"] is True
assert result["CRA"]["subjects:delete"]["allowed"] is False
assert result["CRA"]["subject_aes:create"]["allowed"] is True
assert result["CRA"]["subject_aes:list"]["allowed"] is True
assert result["CRA"]["subject_aes:read"]["allowed"] is True
@pytest.mark.asyncio
@@ -234,7 +259,7 @@ async def test_replace_api_endpoint_permissions_partial_update(db_session: Async
# POST权限应该被更新
assert result["CRA"]["subjects:create"]["allowed"] is False
# GET权限应该保持不变
assert result["CRA"]["subjects:list"]["allowed"] is True
assert result["CRA"]["subjects:read"]["allowed"] is True
@pytest.mark.asyncio