Files
ctms/backend/app/core/api_permissions.py
T
Cheng Zhou 3c66ac9b37 权限系统:完成接口级权限系统第7阶段(迁移第2批模块)
## 主要完成内容

### 1. members 模块迁移
- 迁移5个API端点到接口级权限
- 添加@register_api_endpoint装饰器
- 更新权限配置

### 2. sites 模块迁移
- 迁移4个API端点到接口级权限
- 添加@register_api_endpoint装饰器
- 更新权限配置

### 3. 测试与验证
- 17个集成测试全部通过
- 向后兼容性测试验证通过
- 代码覆盖率85%

### 4. 文档更新
- 更新TESTING_SUMMARY.md
- 创建IMPLEMENTATION_SUMMARY.md

## 统计数据

- 迁移端点数:9个(members 5个 + sites 4个)
- 新增测试:17个
- 总测试数:62个
- 代码覆盖率:85%

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-13 16:37:23 +08:00

313 lines
9.6 KiB
Python

"""接口级权限配置
定义系统中所有API端点的权限配置,支持细粒度的接口级权限控制。
"""
from __future__ import annotations
# 接口级权限配置
# 格式: "METHOD:/path": {配置信息}
API_ENDPOINT_PERMISSIONS = {
# 参与者管理 (subjects)
"POST:/subjects": {
"module": "subjects",
"action": "write",
"description": "创建参与者",
"default_roles": ["PM", "CRA"],
},
"GET:/subjects": {
"module": "subjects",
"action": "read",
"description": "查询参与者列表",
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
},
"GET:/subjects/{id}": {
"module": "subjects",
"action": "read",
"description": "查询参与者详情",
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
},
"PATCH:/subjects/{id}": {
"module": "subjects",
"action": "write",
"description": "更新参与者",
"default_roles": ["PM", "CRA"],
},
"DELETE:/subjects/{id}": {
"module": "subjects",
"action": "write",
"description": "删除参与者",
"default_roles": ["PM"],
},
# 访视管理 (visits)
"POST:/subjects/{subject_id}/visits": {
"module": "subjects",
"action": "write",
"description": "创建访视",
"default_roles": ["PM", "CRA"],
},
"GET:/subjects/{subject_id}/visits": {
"module": "subjects",
"action": "read",
"description": "查询访视列表",
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
},
"GET:/subjects/{subject_id}/visits/{visit_id}": {
"module": "subjects",
"action": "read",
"description": "查询访视详情",
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
},
"PATCH:/subjects/{subject_id}/visits/{visit_id}": {
"module": "subjects",
"action": "write",
"description": "更新访视",
"default_roles": ["PM", "CRA"],
},
# 不良事件 (SAE)
"POST:/risk-issues": {
"module": "risk_issues",
"action": "write",
"description": "创建不良事件",
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"],
},
"GET:/risk-issues": {
"module": "risk_issues",
"action": "read",
"description": "查询不良事件列表",
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
},
"GET:/risk-issues/{id}": {
"module": "risk_issues",
"action": "read",
"description": "查询不良事件详情",
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
},
"PATCH:/risk-issues/{id}": {
"module": "risk_issues",
"action": "write",
"description": "更新不良事件",
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"],
},
"DELETE:/risk-issues/{id}": {
"module": "risk_issues",
"action": "write",
"description": "删除不良事件",
"default_roles": ["PM"],
},
# 财务合同管理 (finance_contracts)
"POST:/finance/contracts": {
"module": "fees",
"action": "write",
"description": "创建财务合同",
"default_roles": ["PM"],
},
"GET:/finance/contracts": {
"module": "fees",
"action": "read",
"description": "查询财务合同列表",
"default_roles": ["PM", "CRA", "IMP"],
},
"GET:/finance/contracts/{id}": {
"module": "fees",
"action": "read",
"description": "查询财务合同详情",
"default_roles": ["PM", "CRA", "IMP"],
},
"PATCH:/finance/contracts/{id}": {
"module": "fees",
"action": "write",
"description": "更新财务合同",
"default_roles": ["PM"],
},
"DELETE:/finance/contracts/{id}": {
"module": "fees",
"action": "write",
"description": "删除财务合同",
"default_roles": ["PM"],
},
# 费用管理 (fees)
"POST:/fees/contracts": {
"module": "fees",
"action": "write",
"description": "创建费用合同",
"default_roles": ["PM"],
},
"GET:/fees/contracts": {
"module": "fees",
"action": "read",
"description": "查询费用合同列表",
"default_roles": ["PM", "CRA", "IMP"],
},
"GET:/fees/contracts/{id}": {
"module": "fees",
"action": "read",
"description": "查询费用合同详情",
"default_roles": ["PM", "CRA", "IMP"],
},
"PATCH:/fees/contracts/{id}": {
"module": "fees",
"action": "write",
"description": "更新费用合同",
"default_roles": ["PM"],
},
"DELETE:/fees/contracts/{id}": {
"module": "fees",
"action": "write",
"description": "删除费用合同",
"default_roles": ["PM"],
},
"POST:/fees/contracts/{id}/payments": {
"module": "fees",
"action": "write",
"description": "创建费用分期",
"default_roles": ["PM"],
},
"PATCH:/fees/payments/{id}": {
"module": "fees",
"action": "write",
"description": "更新费用分期",
"default_roles": ["PM"],
},
"DELETE:/fees/payments/{id}": {
"module": "fees",
"action": "write",
"description": "删除费用分期",
"default_roles": ["PM"],
},
# 项目成员管理
"POST:/studies/{study_id}/members": {
"module": "project_members",
"action": "write",
"description": "添加项目成员",
"default_roles": ["PM"],
},
"GET:/studies/{study_id}/members": {
"module": "project_members",
"action": "read",
"description": "查询项目成员列表",
"default_roles": ["PM"],
},
"GET:/studies/{study_id}/members/candidates": {
"module": "project_members",
"action": "read",
"description": "查询项目成员候选人",
"default_roles": ["PM"],
},
"PATCH:/studies/{study_id}/members/{member_id}": {
"module": "project_members",
"action": "write",
"description": "更新项目成员",
"default_roles": ["PM"],
},
"DELETE:/studies/{study_id}/members/{member_id}": {
"module": "project_members",
"action": "write",
"description": "删除项目成员",
"default_roles": ["PM"],
},
# 中心管理
"POST:/studies/{study_id}/sites": {
"module": "sites",
"action": "write",
"description": "创建中心",
"default_roles": ["PM"],
},
"GET:/studies/{study_id}/sites": {
"module": "sites",
"action": "read",
"description": "查询中心列表",
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
},
"GET:/studies/{study_id}/sites/{site_id}": {
"module": "sites",
"action": "read",
"description": "查询中心详情",
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
},
"PATCH:/studies/{study_id}/sites/{site_id}": {
"module": "sites",
"action": "write",
"description": "更新中心",
"default_roles": ["PM"],
},
"DELETE:/studies/{study_id}/sites/{site_id}": {
"module": "sites",
"action": "write",
"description": "删除中心",
"default_roles": ["PM"],
},
}
# 向后兼容:模块级权限到接口级权限的映射
# 用于在接口级权限未配置时,回退到模块级权限
MODULE_TO_ENDPOINTS: dict[str, dict[str, list[str]]] = {
"subjects": {
"read": [
"GET:/subjects",
"GET:/subjects/{id}",
"GET:/subjects/{subject_id}/visits",
"GET:/subjects/{subject_id}/visits/{visit_id}",
],
"write": [
"POST:/subjects",
"PATCH:/subjects/{id}",
"DELETE:/subjects/{id}",
"POST:/subjects/{subject_id}/visits",
"PATCH:/subjects/{subject_id}/visits/{visit_id}",
],
},
"risk_issues": {
"read": [
"GET:/risk-issues",
"GET:/risk-issues/{id}",
],
"write": [
"POST:/risk-issues",
"PATCH:/risk-issues/{id}",
"DELETE:/risk-issues/{id}",
],
},
"fees": {
"read": [
"GET:/fees/contracts",
"GET:/fees/contracts/{id}",
"GET:/finance/contracts",
"GET:/finance/contracts/{id}",
],
"write": [
"POST:/fees/contracts",
"PATCH:/fees/contracts/{id}",
"DELETE:/fees/contracts/{id}",
"POST:/fees/contracts/{id}/payments",
"PATCH:/fees/payments/{id}",
"DELETE:/fees/payments/{id}",
"POST:/finance/contracts",
"PATCH:/finance/contracts/{id}",
"DELETE:/finance/contracts/{id}",
],
},
"project_members": {
"read": [
"GET:/studies/{study_id}/members",
"GET:/studies/{study_id}/members/candidates",
],
"write": [
"POST:/studies/{study_id}/members",
"PATCH:/studies/{study_id}/members/{member_id}",
"DELETE:/studies/{study_id}/members/{member_id}",
],
},
"sites": {
"read": [
"GET:/studies/{study_id}/sites",
"GET:/studies/{study_id}/sites/{site_id}",
],
"write": [
"POST:/studies/{study_id}/sites",
"PATCH:/studies/{study_id}/sites/{site_id}",
"DELETE:/studies/{study_id}/sites/{site_id}",
],
},
}