移除QA预设角色并收紧角色模板编辑
This commit is contained in:
@@ -18,7 +18,6 @@ router = APIRouter()
|
||||
PROJECT_ROLE_RANK = {
|
||||
"ADMIN": 100,
|
||||
"PM": 80,
|
||||
"QA": 60,
|
||||
"PV": 50,
|
||||
"MEDICAL_REVIEW": 50,
|
||||
"CRA": 40,
|
||||
|
||||
@@ -25,6 +25,8 @@ def _normalize_active_roles(value: object) -> list[str]:
|
||||
continue
|
||||
if role == "ADMIN":
|
||||
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="ADMIN 不能作为项目角色")
|
||||
if role == "QA":
|
||||
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="QA 不能作为项目角色")
|
||||
if len(role) > 20:
|
||||
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="项目角色长度不能超过20个字符")
|
||||
if role not in roles:
|
||||
|
||||
@@ -20,14 +20,14 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "subjects",
|
||||
"action": "read",
|
||||
"description": "查询参与者列表",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
"prerequisite_permissions": [],
|
||||
},
|
||||
"subjects:read": {
|
||||
"module": "subjects",
|
||||
"action": "read",
|
||||
"description": "查询参与者详情",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
"prerequisite_permissions": [],
|
||||
},
|
||||
"subjects:update": {
|
||||
@@ -56,14 +56,14 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "subjects",
|
||||
"action": "read",
|
||||
"description": "查询访视列表",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
"prerequisite_permissions": [],
|
||||
},
|
||||
"visits:read": {
|
||||
"module": "subjects",
|
||||
"action": "read",
|
||||
"description": "查询访视详情",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
"prerequisite_permissions": [],
|
||||
},
|
||||
"visits:update": {
|
||||
@@ -92,14 +92,14 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "risk_issues",
|
||||
"action": "read",
|
||||
"description": "查询不良事件列表",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
"prerequisite_permissions": [],
|
||||
},
|
||||
"risk_issues:read": {
|
||||
"module": "risk_issues",
|
||||
"action": "read",
|
||||
"description": "查询不良事件详情",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
"prerequisite_permissions": [],
|
||||
},
|
||||
"risk_issues:update": {
|
||||
@@ -251,13 +251,13 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "sites",
|
||||
"action": "read",
|
||||
"description": "查询中心列表",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
},
|
||||
"sites:read": {
|
||||
"module": "sites",
|
||||
"action": "read",
|
||||
"description": "查询中心详情",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
},
|
||||
"sites:update": {
|
||||
"module": "sites",
|
||||
@@ -413,7 +413,7 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "project_overview",
|
||||
"action": "read",
|
||||
"description": "查询项目概览",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
},
|
||||
# 监查问题管理
|
||||
"monitoring_issues:create": {
|
||||
@@ -470,7 +470,7 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "monitoring_audit",
|
||||
"action": "read",
|
||||
"description": "查询监查访视问题",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW"],
|
||||
"prerequisite_permissions": [],
|
||||
},
|
||||
"monitoring_audit:update": {
|
||||
@@ -499,14 +499,14 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "materials",
|
||||
"action": "read",
|
||||
"description": "查询药物发货列表",
|
||||
"default_roles": ["PM", "CRA", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "IMP"],
|
||||
"prerequisite_permissions": [],
|
||||
},
|
||||
"drug_shipments:read": {
|
||||
"module": "materials",
|
||||
"action": "read",
|
||||
"description": "查询药物发货详情",
|
||||
"default_roles": ["PM", "CRA", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "IMP"],
|
||||
"prerequisite_permissions": [],
|
||||
},
|
||||
"drug_shipments:update": {
|
||||
@@ -534,13 +534,13 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "materials",
|
||||
"action": "read",
|
||||
"description": "查询物资列表",
|
||||
"default_roles": ["PM", "CRA", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "IMP"],
|
||||
},
|
||||
"materials:read": {
|
||||
"module": "materials",
|
||||
"action": "read",
|
||||
"description": "查询物资详情",
|
||||
"default_roles": ["PM", "CRA", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "IMP"],
|
||||
},
|
||||
"materials:update": {
|
||||
"module": "materials",
|
||||
@@ -595,19 +595,19 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "audit_export",
|
||||
"action": "read",
|
||||
"description": "查询审计日志列表",
|
||||
"default_roles": ["PM", "QA"],
|
||||
"default_roles": ["PM"],
|
||||
},
|
||||
"audit_logs:read": {
|
||||
"module": "audit_export",
|
||||
"action": "read",
|
||||
"description": "查询审计日志详情",
|
||||
"default_roles": ["PM", "QA"],
|
||||
"default_roles": ["PM"],
|
||||
},
|
||||
"audit_logs:export": {
|
||||
"module": "audit_export",
|
||||
"action": "read",
|
||||
"description": "导出审计日志",
|
||||
"default_roles": ["PM", "QA"],
|
||||
"default_roles": ["PM"],
|
||||
},
|
||||
# 知识库笔记管理
|
||||
"knowledge_notes:create": {
|
||||
@@ -620,13 +620,13 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "shared_library",
|
||||
"action": "read",
|
||||
"description": "查询知识库笔记列表",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
},
|
||||
"knowledge_notes:read": {
|
||||
"module": "shared_library",
|
||||
"action": "read",
|
||||
"description": "查询知识库笔记详情",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
},
|
||||
"knowledge_notes:update": {
|
||||
"module": "shared_library",
|
||||
@@ -676,7 +676,7 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "setup_config",
|
||||
"action": "read",
|
||||
"description": "查询立项配置",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
},
|
||||
"setup_config:update": {
|
||||
"module": "setup_config",
|
||||
@@ -707,7 +707,7 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "project_milestones",
|
||||
"action": "read",
|
||||
"description": "查询项目里程碑列表",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
},
|
||||
"project_milestones:update": {
|
||||
"module": "project_milestones",
|
||||
@@ -751,7 +751,7 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "attachments",
|
||||
"action": "read",
|
||||
"description": "查询附件",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
},
|
||||
"attachments:update": {
|
||||
"module": "attachments",
|
||||
@@ -795,7 +795,7 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "faq",
|
||||
"action": "read",
|
||||
"description": "查询FAQ",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
},
|
||||
"faq:update": {
|
||||
"module": "faq",
|
||||
@@ -820,7 +820,7 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "faq",
|
||||
"action": "read",
|
||||
"description": "查询FAQ分类",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
},
|
||||
"faq_category:update": {
|
||||
"module": "faq",
|
||||
@@ -852,7 +852,7 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "dashboard",
|
||||
"action": "read",
|
||||
"description": "查询仪表板",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
"prerequisite_permissions": ["sites:read", "subjects:read"],
|
||||
},
|
||||
# 参与者历史管理
|
||||
@@ -867,14 +867,14 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "subject_histories",
|
||||
"action": "read",
|
||||
"description": "查询参与者历史列表",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
"prerequisite_permissions": [],
|
||||
},
|
||||
"subject_histories:read": {
|
||||
"module": "subject_histories",
|
||||
"action": "read",
|
||||
"description": "查询参与者历史详情",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
"prerequisite_permissions": [],
|
||||
},
|
||||
"subject_histories:update": {
|
||||
@@ -902,13 +902,13 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "material_equipments",
|
||||
"action": "read",
|
||||
"description": "查询物资设备列表",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
},
|
||||
"material_equipments:read": {
|
||||
"module": "material_equipments",
|
||||
"action": "read",
|
||||
"description": "查询物资设备详情",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
},
|
||||
"material_equipments:update": {
|
||||
"module": "material_equipments",
|
||||
@@ -933,7 +933,7 @@ API_ENDPOINT_PERMISSIONS = {
|
||||
"module": "documents",
|
||||
"action": "read",
|
||||
"description": "查询文档",
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"],
|
||||
"default_roles": ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"],
|
||||
},
|
||||
"documents:update": {
|
||||
"module": "documents",
|
||||
@@ -1170,7 +1170,7 @@ OPERATION_TO_ENDPOINTS: dict[str, dict[str, list[str]]] = {
|
||||
}
|
||||
|
||||
# 项目级权限角色列表
|
||||
PROJECT_PERMISSION_ROLES = ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"]
|
||||
PROJECT_PERMISSION_ROLES = ["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"]
|
||||
|
||||
# 前置权限映射表(用于权限检查)
|
||||
# 定义每个操作需要的前置权限
|
||||
|
||||
@@ -7,7 +7,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.api_endpoint_permission import ApiEndpointPermission
|
||||
from app.models.study import Study
|
||||
from app.core.api_permissions import API_ENDPOINT_PERMISSIONS, OPERATION_PREREQUISITES
|
||||
from app.core.api_permissions import API_ENDPOINT_PERMISSIONS, OPERATION_PREREQUISITES, PROJECT_PERMISSION_ROLES
|
||||
from app.core.permission_cache import get_permission_cache
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ async def get_api_endpoint_permissions(
|
||||
study_result = await db.execute(select(Study).where(Study.id == study_id))
|
||||
study = study_result.scalar_one_or_none()
|
||||
active_roles = [role for role in (study.active_roles if study else []) if isinstance(role, str) and role.strip()]
|
||||
roles = list(dict.fromkeys(["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA", *active_roles, *overrides.keys()]))
|
||||
roles = list(dict.fromkeys([*PROJECT_PERMISSION_ROLES, *active_roles, *overrides.keys()]))
|
||||
matrix: dict[str, dict[str, dict[str, bool]]] = {}
|
||||
for role in roles:
|
||||
matrix[role] = {}
|
||||
|
||||
@@ -22,7 +22,6 @@ ROLE_ACTIONS: dict[str, set[str]] = {
|
||||
"ack",
|
||||
"delete_document",
|
||||
},
|
||||
"QA": {"view", "create_document", "create_version", "submit", "approve", "distribute", "ack"},
|
||||
"PM": {"view", "create_document", "create_version", "submit", "approve", "distribute", "ack", "delete_document"},
|
||||
"CRA": {"view", "create_version", "submit", "ack"},
|
||||
"PV": {"view", "create_version", "submit", "ack"},
|
||||
|
||||
@@ -19,7 +19,6 @@ class UserRole(str, enum.Enum):
|
||||
PV = "PV"
|
||||
MEDICAL_REVIEW = "MEDICAL_REVIEW"
|
||||
IMP = "IMP"
|
||||
QA = "QA"
|
||||
|
||||
|
||||
class UserStatus(str, enum.Enum):
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Literal
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
ProjectPermissionRole = Literal["ADMIN", "PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA"]
|
||||
ProjectPermissionRole = Literal["ADMIN", "PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP"]
|
||||
ProjectPermissionAction = Literal["read", "write"]
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Literal, Optional
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator
|
||||
|
||||
UserRole = Literal["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "QA", "ADMIN"]
|
||||
UserRole = Literal["PM", "CRA", "PV", "MEDICAL_REVIEW", "IMP", "ADMIN"]
|
||||
UserStatus = Literal["PENDING", "ACTIVE", "REJECTED", "DISABLED"]
|
||||
|
||||
PASSWORD_REGEX = re.compile(r"^(?=.*[A-Za-z])(?=.*\d).{8,}$")
|
||||
|
||||
@@ -7,7 +7,7 @@ from sqlalchemy.orm import selectinload
|
||||
|
||||
from app.models.permission_template import PermissionTemplate, PermissionTemplateVersion, TemplateType
|
||||
from app.models.api_endpoint_permission import ApiEndpointPermission
|
||||
from app.core.api_permissions import API_ENDPOINT_PERMISSIONS
|
||||
from app.core.api_permissions import API_ENDPOINT_PERMISSIONS, PROJECT_PERMISSION_ROLES
|
||||
from app.schemas.permission_template import PermissionTemplateCreate, PermissionTemplateUpdate
|
||||
|
||||
|
||||
@@ -21,6 +21,9 @@ class PermissionTemplateService:
|
||||
created_by: UUID,
|
||||
) -> PermissionTemplate:
|
||||
"""创建权限模板"""
|
||||
if payload.template_type != TemplateType.CUSTOM:
|
||||
raise ValueError("只能新增自定义角色")
|
||||
|
||||
# 验证权限配置
|
||||
await PermissionTemplateService._validate_permissions(payload.permissions)
|
||||
|
||||
@@ -78,6 +81,11 @@ class PermissionTemplateService:
|
||||
query = query.where(PermissionTemplate.template_type == template_type)
|
||||
if category:
|
||||
query = query.where(PermissionTemplate.category == category)
|
||||
elif template_type == TemplateType.ROLE:
|
||||
query = query.where(
|
||||
(PermissionTemplate.is_system.is_(False))
|
||||
| (PermissionTemplate.category.in_(PROJECT_PERMISSION_ROLES))
|
||||
)
|
||||
|
||||
query = query.offset(skip).limit(limit)
|
||||
result = await db.execute(query)
|
||||
@@ -94,9 +102,11 @@ class PermissionTemplateService:
|
||||
if not template:
|
||||
raise ValueError(f"模板 {template_id} 不存在")
|
||||
|
||||
# 不允许修改系统预设模板
|
||||
# 系统预设模板只允许更新展示元数据,权限配置和分类等结构字段保持受保护。
|
||||
if template.is_system:
|
||||
raise ValueError("不允许修改系统预设模板")
|
||||
protected_fields = {"permissions", "tags", "category", "recommended_roles"}
|
||||
if protected_fields.intersection(payload.model_fields_set):
|
||||
raise ValueError("不允许修改系统预设模板")
|
||||
|
||||
# 验证权限配置
|
||||
if payload.permissions:
|
||||
|
||||
Reference in New Issue
Block a user