权限管理:接入项目级权限矩阵
新增项目级角色权限矩阵,覆盖 PM、CRA、PV、IMP、QA 等项目角色,并通过迁移表持久化各业务模块的读写权限。 后端将参与者、合同费用、物资、启动、里程碑、风险问题、监查稽查、FAQ、共享库、文件版本等接口接入矩阵校验,修复审计日志导出权限和登录 502 的依赖导入问题。 前端新增权限管理页面和项目路由权限映射,按矩阵控制导航显示、直接访问拦截、操作按钮启停,并限制管理后台仅 admin 和授权 PM 可进入。 补充后端权限归一化与接口静态测试、前端路由/权限工具/权限管理页面/工作台等回归测试。
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
from app.core.project_permissions import PROJECT_PERMISSION_MODULES, normalize_permission_matrix
|
||||
|
||||
|
||||
def test_normalize_permission_matrix_makes_admin_fixed_full_access():
|
||||
matrix = normalize_permission_matrix({"ADMIN": {}})
|
||||
|
||||
for module in PROJECT_PERMISSION_MODULES:
|
||||
assert matrix["ADMIN"][module["key"]] == {
|
||||
"read": True,
|
||||
"write": module.get("writable") is not False,
|
||||
}
|
||||
|
||||
|
||||
def test_normalize_permission_matrix_keeps_admin_fixed_even_when_disabled():
|
||||
payload = {
|
||||
"ADMIN": {
|
||||
module["key"]: {"read": False, "write": False}
|
||||
for module in PROJECT_PERMISSION_MODULES
|
||||
}
|
||||
}
|
||||
|
||||
matrix = normalize_permission_matrix(payload)
|
||||
|
||||
for module in PROJECT_PERMISSION_MODULES:
|
||||
assert matrix["ADMIN"][module["key"]] == {
|
||||
"read": True,
|
||||
"write": module.get("writable") is not False,
|
||||
}
|
||||
|
||||
|
||||
def test_normalize_permission_matrix_write_implies_read():
|
||||
matrix = normalize_permission_matrix({
|
||||
"CRA": {
|
||||
"subjects": {"read": False, "write": True},
|
||||
}
|
||||
})
|
||||
|
||||
assert matrix["CRA"]["subjects"] == {"read": True, "write": True}
|
||||
|
||||
|
||||
def test_business_permission_modules_follow_project_menu():
|
||||
module_labels = [module["label"] for module in PROJECT_PERMISSION_MODULES]
|
||||
|
||||
assert module_labels == [
|
||||
"项目成员",
|
||||
"中心管理",
|
||||
"审计日志导出",
|
||||
"项目总览",
|
||||
"项目里程碑",
|
||||
"合同费用管理",
|
||||
"物资管理",
|
||||
"文件版本管理",
|
||||
"立项与伦理",
|
||||
"启动与授权",
|
||||
"参与者管理",
|
||||
"风险问题",
|
||||
"监查稽查",
|
||||
"eTMF",
|
||||
"FAQ",
|
||||
"共享库",
|
||||
]
|
||||
|
||||
|
||||
def test_default_business_permissions_match_role_responsibilities():
|
||||
matrix = normalize_permission_matrix()
|
||||
|
||||
assert matrix["PM"]["project_members"] == {"read": True, "write": True}
|
||||
assert matrix["PM"]["sites"] == {"read": True, "write": True}
|
||||
assert matrix["CRA"]["project_members"] == {"read": False, "write": False}
|
||||
assert matrix["CRA"]["audit_export"] == {"read": False, "write": False}
|
||||
assert matrix["PV"]["sites"] == {"read": False, "write": False}
|
||||
assert matrix["IMP"]["audit_export"] == {"read": False, "write": False}
|
||||
assert matrix["QA"]["audit_export"] == {"read": False, "write": False}
|
||||
assert matrix["CRA"]["project_milestones"] == {"read": True, "write": True}
|
||||
assert matrix["CRA"]["subjects"] == {"read": True, "write": True}
|
||||
assert matrix["CRA"]["monitoring_audit"] == {"read": True, "write": True}
|
||||
assert matrix["PV"]["risk_issues"] == {"read": True, "write": True}
|
||||
assert matrix["IMP"]["materials"] == {"read": True, "write": True}
|
||||
assert matrix["PM"]["faq"] == {"read": True, "write": True}
|
||||
assert matrix["CRA"]["faq"] == {"read": True, "write": True}
|
||||
assert matrix["PM"]["project_overview"] == {"read": True, "write": False}
|
||||
assert matrix["CRA"]["project_overview"] == {"read": True, "write": False}
|
||||
assert matrix["PM"]["shared_library"] == {"read": True, "write": True}
|
||||
assert matrix["CRA"]["shared_library"] == {"read": True, "write": True}
|
||||
|
||||
|
||||
def test_project_overview_is_read_only_even_when_write_is_submitted():
|
||||
matrix = normalize_permission_matrix({
|
||||
"CRA": {
|
||||
"project_overview": {"read": False, "write": True},
|
||||
}
|
||||
})
|
||||
|
||||
assert matrix["CRA"]["project_overview"] == {"read": True, "write": False}
|
||||
|
||||
|
||||
def test_pm_can_be_granted_management_backend_permissions():
|
||||
matrix = normalize_permission_matrix({
|
||||
"PM": {
|
||||
"project_members": {"read": True, "write": True},
|
||||
"sites": {"read": True, "write": True},
|
||||
"audit_export": {"read": True, "write": True},
|
||||
}
|
||||
})
|
||||
|
||||
assert matrix["PM"]["project_members"] == {"read": True, "write": True}
|
||||
assert matrix["PM"]["sites"] == {"read": True, "write": True}
|
||||
assert matrix["PM"]["audit_export"] == {"read": True, "write": True}
|
||||
|
||||
|
||||
def test_non_pm_roles_cannot_be_granted_management_backend_permissions():
|
||||
management_modules = ["project_members", "sites", "audit_export"]
|
||||
matrix = normalize_permission_matrix({
|
||||
role: {
|
||||
module: {"read": True, "write": True}
|
||||
for module in management_modules
|
||||
}
|
||||
for role in ["CRA", "PV", "IMP", "QA"]
|
||||
})
|
||||
|
||||
for role in ["CRA", "PV", "IMP", "QA"]:
|
||||
for module in management_modules:
|
||||
assert matrix[role][module] == {"read": False, "write": False}
|
||||
|
||||
|
||||
def test_each_non_admin_role_can_toggle_each_writable_module():
|
||||
roles = ["PM", "CRA", "PV", "IMP", "QA"]
|
||||
management_modules = {"project_members", "sites", "audit_export"}
|
||||
writable_modules = [
|
||||
module["key"]
|
||||
for module in PROJECT_PERMISSION_MODULES
|
||||
if module.get("writable") is not False and module["key"] not in management_modules
|
||||
]
|
||||
|
||||
for role in roles:
|
||||
enabled = normalize_permission_matrix({
|
||||
role: {
|
||||
module: {"read": False, "write": True}
|
||||
for module in writable_modules
|
||||
}
|
||||
})
|
||||
disabled = normalize_permission_matrix({
|
||||
role: {
|
||||
module["key"]: {"read": False, "write": False}
|
||||
for module in PROJECT_PERMISSION_MODULES
|
||||
}
|
||||
})
|
||||
|
||||
for module in writable_modules:
|
||||
assert enabled[role][module] == {"read": True, "write": True}
|
||||
assert disabled[role][module] == {"read": False, "write": False}
|
||||
|
||||
|
||||
def test_read_only_modules_never_accept_write_for_any_role():
|
||||
read_only_modules = [
|
||||
module["key"]
|
||||
for module in PROJECT_PERMISSION_MODULES
|
||||
if module.get("writable") is False
|
||||
]
|
||||
payload = {
|
||||
role: {
|
||||
module: {"read": False, "write": True}
|
||||
for module in read_only_modules
|
||||
}
|
||||
for role in ["ADMIN", "PM", "CRA", "PV", "IMP", "QA"]
|
||||
}
|
||||
|
||||
matrix = normalize_permission_matrix(payload)
|
||||
|
||||
for role in ["ADMIN", "PM", "CRA", "PV", "IMP", "QA"]:
|
||||
for module in read_only_modules:
|
||||
assert matrix[role][module] == {"read": True, "write": False}
|
||||
Reference in New Issue
Block a user