feat(v0.7.1): P0 修复 + 企微 SSO + RBAC 细粒度 + audit_log
P0 修复: - /api/ready import 错误 (_get_engine + settings.create_redis_client) - 删 agent.otp_secret/otp_enabled 双字段 (migration 026) - 重建 021_rbac migration (IF NOT EXISTS 兼容) P1 新增: - 企微 SSO (auth_wecom_sso.py, useWeChatWorkSSO composable, PortalSelect UA 检测) - RBAC 5 角色 × 4 资源 × 4 操作 × 3 范围 (rbac_service + seed_rbac + require_permission) - audit_log 模型 + migration 027 + 服务 + API - 管理后台 RBAC 权限矩阵 UI (PermissionsMatrix.vue) 质量: - pytest 405 passed / 33 pre-existing failed / 4 xfailed (v0.7.1 引入失败 = 0) - conftest GBK patch 强制 UTF-8 读 .env - .gitignore 排除 *.b64 (含 admin token 凭据) - DEPLOY-v0.7.1.md 7 步 runbook + 4 坑 + 回滚预案
This commit is contained in:
@@ -284,6 +284,110 @@ def require_admin(func):
|
||||
return require_role("admin")(func)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 细粒度权限装饰器 (v0.7.1 task #86 — RBAC 5 角色 × 4 资源 × 4 操作 × 3 范围)
|
||||
# =============================================================================
|
||||
# 权限字符串格式: "resource:action:scope"
|
||||
# 例: "conversation:read:all"
|
||||
#
|
||||
# 用法:
|
||||
# @router.get("/api/admin/agents")
|
||||
# @require_permission("agent:read:all")
|
||||
# async def list_agents(...): ...
|
||||
#
|
||||
# 行为:
|
||||
# 1. 装饰器只检查"是否拥有权限字符串",不直接执行 DB 查询
|
||||
# 2. 实际检查在 rbac_service.check_permission() 里
|
||||
# 3. 用户的权限从 UserInfo.permissions 字段读(由 get_current_user 解析 token 时填入)
|
||||
# =============================================================================
|
||||
|
||||
def require_permission(
|
||||
resource: str,
|
||||
action: str,
|
||||
scope: str = "own",
|
||||
):
|
||||
"""细粒度权限验证装饰器(v0.7.1 task #86)。
|
||||
|
||||
Args:
|
||||
resource: 资源(conversation/agent/system_config/audit_log)
|
||||
action: 操作(read/create/update/delete)
|
||||
scope: 数据范围(own/department/all)
|
||||
|
||||
Example:
|
||||
@router.get("/api/admin/agents")
|
||||
@require_permission("agent", "read", "all")
|
||||
async def list_agents(current_user: UserInfo = Depends(get_current_user)):
|
||||
...
|
||||
"""
|
||||
perm_string = f"{resource}:{action}:{scope}"
|
||||
|
||||
def decorator(func):
|
||||
sig = inspect.signature(func)
|
||||
params = list(sig.parameters.values())
|
||||
params.append(
|
||||
inspect.Parameter(
|
||||
'current_user',
|
||||
inspect.Parameter.KEYWORD_ONLY,
|
||||
annotation=UserInfo,
|
||||
default=Depends(get_current_user),
|
||||
)
|
||||
)
|
||||
new_sig = sig.replace(parameters=params)
|
||||
|
||||
@wraps(func)
|
||||
async def wrapper(*args, **kwargs):
|
||||
current_user = kwargs.pop('current_user')
|
||||
|
||||
# 拉用户所有角色的 permissions
|
||||
# 注: UserInfo.roles 是角色名列表,permissions 是 {role: [perm]} 字典
|
||||
# 首次实现简化: 角色判断 + admin 通配符
|
||||
# 完整实现需要查 DB 拉 permissions,见 rbac_service.check_permission
|
||||
|
||||
user_roles = set(current_user.roles or [])
|
||||
|
||||
# 1. admin 角色直通(通配符 *:*:all)
|
||||
if "admin" in user_roles:
|
||||
return await func(*args, current_user=current_user, **kwargs)
|
||||
|
||||
# 2. 其他角色: 走 rbac_service.check_permission
|
||||
# 简化: 这里只看角色名,不查 DB(性能考虑)
|
||||
# 实际生产可加缓存或预加载到 token
|
||||
from app.services.rbac_service import (
|
||||
ROLE_PERMISSIONS,
|
||||
check_permission,
|
||||
)
|
||||
# 把 ROLE_PERMISSIONS 转成 {role_name: [perm_string]} 格式
|
||||
user_perms_dict = {
|
||||
role: [f"{r}:{a}:{s}" for (r, a, s) in perms]
|
||||
for role, perms in ROLE_PERMISSIONS.items()
|
||||
}
|
||||
|
||||
has_perm = check_permission(
|
||||
user_roles=list(user_roles),
|
||||
user_permissions=user_perms_dict,
|
||||
required_resource=resource,
|
||||
required_action=action,
|
||||
required_scope=scope,
|
||||
)
|
||||
|
||||
if not has_perm:
|
||||
logger.warning(
|
||||
f"用户 {current_user.employee_id} 权限不足: "
|
||||
f"角色 {list(user_roles)}, 缺 {perm_string}"
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=f"权限不足: 需要 {perm_string}",
|
||||
)
|
||||
|
||||
return await func(*args, current_user=current_user, **kwargs)
|
||||
|
||||
wrapper.__signature__ = new_sig
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 高危操作 OTP 守卫依赖(Phase 1.3 task #19)
|
||||
# =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user