feat: 平台治理与权限体系完善,存储进度/GPU预留/审批中心与日志整合

- 平台治理: 租户用户权限层次、资源ACL、审批中心与审批模板、访问申请
- 存储: MinIO 存储进度迁移、对象存储安全加固与测试
- 计算: GPU 资源预留、compute 轮询与同步增强
- 权限: permission v2 迁移、权限安全验收测试
- 日志: 后端运行日志中文说明、操作日志整合
- 数据处理/评测: 数据转换与模型评测优化

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
wuyongtao
2026-08-21 09:49:48 +08:00
parent 080ef6ab00
commit 6f0e82f351
94 changed files with 9547 additions and 1045 deletions

View File

@@ -5,16 +5,21 @@ from typing import Any
from app.api.v1.endpoints.platform import ok, fail
from app.db.platform_store import get_platform_store
from app.core.auth import get_current_user, has_resource_access, is_admin
from app.core.auth import (
get_current_user,
has_resource_access,
is_admin,
resource_record,
resource_tenant_id,
user_tenant_ids,
)
from app.core.audit import audit_log, AuditActions
router = APIRouter(prefix="/resources", tags=["resource"])
def _actor(request: Request) -> str | None:
auth = request.headers.get("Authorization", "")
token = auth.replace("Bearer ", "").strip()
return token or None
def _actor(request: Request, current_user: dict[str, Any]) -> str | None:
return str(current_user.get("id") or "") or None
@router.get("/{resource_type}/{resource_id}/acl")
@@ -39,19 +44,48 @@ def set_acl(
current_user: dict = Depends(get_current_user),
) -> dict[str, Any]:
"""设置资源 ACLbody: { entries: [{ subject_type, subject_id, permissions: [] }] }"""
resource = resource_record(resource_type, resource_id)
if not resource and not is_admin(current_user):
raise fail(404, "resource not found")
if not is_admin(current_user) and not has_resource_access(resource_type, resource_id, current_user, "write"):
raise fail(403, "only resource owner or admin can update ACL")
entries = payload.get("entries") or []
allowed = {"read", "write", "execute", "download", "delete", "admin"}
owner_allowed = {"read", "write", "execute", "download"}
tenant_id = resource_tenant_id(resource_type, resource) if resource else None
tenant_ids = user_tenant_ids(current_user)
for entry in entries:
if entry.get("principal_type") not in {"user", "role"} or not entry.get("principal_id"):
raise fail(400, "invalid ACL principal")
if any(permission not in allowed for permission in entry.get("permissions") or []):
permissions = set(entry.get("permissions") or [])
if any(permission not in allowed for permission in permissions):
raise fail(400, "invalid ACL permission")
result = get_platform_store().set_resource_acl(resource_type, resource_id, entries)
if not is_admin(current_user) and permissions - owner_allowed:
raise fail(403, "resource owners cannot grant delete or admin permission")
if entry.get("principal_type") == "user":
with get_platform_store().connect() as conn:
principal = conn.execute(
"SELECT id, tenant_id, status FROM users WHERE id=?",
(entry["principal_id"],),
).fetchone()
if not principal or principal.get("status") != "active":
raise fail(400, "ACL user does not exist or is inactive")
principal_tenant = str(principal.get("tenant_id") or "default")
if not is_admin(current_user) and tenant_id and principal_tenant not in tenant_ids:
raise fail(403, "cannot grant resource access across tenants")
elif not is_admin(current_user):
# Role ACLs are global in the legacy schema and therefore cannot
# be safely scoped to one tenant by a normal resource owner.
raise fail(403, "only administrators can grant role-based ACLs")
result = get_platform_store().set_resource_acl(
resource_type,
resource_id,
entries,
granted_by=str(current_user.get("id") or "") or None,
)
get_platform_store().record_audit(
action="resource.acl.set",
actor_id=_actor(request) if request else None,
actor_id=_actor(request, current_user) if request else current_user.get("id"),
target_type=resource_type,
target_id=resource_id,
detail=f"entries={len(entries)}",