权限监控与管理界面全面美化
重新设计权限系统所有 UI 组件的视觉风格,统一配色、圆角、阴影和交互动效: - 实时概览:统计卡片加图标和渐变色条,健康评分改为环形进度,告警改为卡片式 - 趋势分析:图表卡片加彩色图标标识,ECharts 配色升级为渐变面积填充 - 访问日志:指标卡片带图标,日志审计改为卡片式入口,IP排行前三高亮 - IP属地:工具栏重设计,排行列表前三渐变高亮,指标卡片统一新风格 - 系统级权限:从 el-table 改为自定义卡片列表,模块块独立圆角卡片 - 项目权限配置:空状态引导优化,成员表格加头像,工具栏加背景容器 - 角色概览卡片:加进度条可视化,hover 微动效 - 接口权限矩阵:工具栏分离布局,表格圆角包裹 - 角色管理抽屉:侧边栏选中态渐变,操作行 hover 高亮 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
from typing import Annotated, AsyncGenerator, Callable, Iterable
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from fastapi import Depends, HTTPException, Request, status
|
||||
@@ -129,12 +130,17 @@ def require_api_permission(endpoint_key: str, *, allow_system_admin: bool = True
|
||||
check_prerequisites: 是否检查前置权限
|
||||
"""
|
||||
async def dependency(
|
||||
request: Request,
|
||||
study_id: uuid.UUID,
|
||||
current_user=Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
):
|
||||
from app.core.permission_monitor import get_permission_monitor
|
||||
role_value = current_user.role.value if hasattr(current_user.role, "value") else current_user.role
|
||||
if allow_system_admin and role_value == "ADMIN":
|
||||
_enqueue_permission_log(
|
||||
study_id, current_user.id, endpoint_key, "ADMIN", True, 0.0, request
|
||||
)
|
||||
return current_user
|
||||
membership = await member_crud.get_member(db, study_id, current_user.id)
|
||||
if not membership or not membership.is_active:
|
||||
@@ -143,11 +149,29 @@ def require_api_permission(endpoint_key: str, *, allow_system_admin: bool = True
|
||||
message="不是该项目成员",
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
allowed = await role_has_api_permission(
|
||||
db, study_id, membership.role_in_study, endpoint_key, check_prerequisites=check_prerequisites
|
||||
)
|
||||
|
||||
start_time = time.perf_counter()
|
||||
error = None
|
||||
try:
|
||||
allowed = await role_has_api_permission(
|
||||
db, study_id, membership.role_in_study, endpoint_key, check_prerequisites=check_prerequisites
|
||||
)
|
||||
except Exception as e:
|
||||
error = e
|
||||
allowed = False
|
||||
raise
|
||||
finally:
|
||||
elapsed_ms = (time.perf_counter() - start_time) * 1000
|
||||
monitor = get_permission_monitor()
|
||||
monitor.record_permission_check(
|
||||
allowed=allowed, elapsed_time=elapsed_ms / 1000, error=error
|
||||
)
|
||||
_enqueue_permission_log(
|
||||
study_id, current_user.id, endpoint_key,
|
||||
membership.role_in_study, allowed, elapsed_ms, request
|
||||
)
|
||||
|
||||
if not allowed:
|
||||
# 获取缺失的前置权限,用于错误提示
|
||||
missing_prereqs = await get_missing_prerequisites(
|
||||
db, study_id, membership.role_in_study, endpoint_key
|
||||
)
|
||||
@@ -168,6 +192,32 @@ def require_api_permission(endpoint_key: str, *, allow_system_admin: bool = True
|
||||
return dependency
|
||||
|
||||
|
||||
def _enqueue_permission_log(
|
||||
study_id: uuid.UUID,
|
||||
user_id: uuid.UUID,
|
||||
endpoint_key: str,
|
||||
role: str,
|
||||
allowed: bool,
|
||||
elapsed_ms: float,
|
||||
request: Request,
|
||||
) -> None:
|
||||
from app.services.permission_log_writer import get_log_writer
|
||||
|
||||
writer = get_log_writer()
|
||||
if writer:
|
||||
forwarded = request.headers.get("x-forwarded-for")
|
||||
ip = forwarded.split(",")[0].strip() if forwarded else (request.client.host if request.client else None)
|
||||
writer.enqueue({
|
||||
"study_id": study_id,
|
||||
"user_id": user_id,
|
||||
"endpoint_key": endpoint_key,
|
||||
"role": role,
|
||||
"allowed": allowed,
|
||||
"elapsed_ms": elapsed_ms,
|
||||
"ip_address": ip,
|
||||
})
|
||||
|
||||
|
||||
async def get_cra_site_scope(
|
||||
db: AsyncSession,
|
||||
study_id: uuid.UUID,
|
||||
|
||||
Reference in New Issue
Block a user