"""Reuse Next's product identity; no new user store or client-selected identity.""" from dataclasses import dataclass from uuid import UUID import httpx from fastapi import Request from .errors import SampleFailure, ensure @dataclass(frozen=True) class Identity: project_id: str user_id: str can_write: bool project_code: str = "" def next_get(request: Request, path: str, params=None): settings = request.app.state.settings try: with httpx.Client(base_url=settings.next_server_url, timeout=settings.timeout_seconds, follow_redirects=False, trust_env=False) as client: response = client.get(path, params=params, headers={ "Authorization": request.headers.get("authorization", ""), "X-Project-Id": request.headers.get("x-project-id", ""), }) if response.status_code in {401, 403, 404}: raise SampleFailure("SOURCE_ACCESS_DENIED", "Next 拒绝身份或来源对象访问", response.status_code) ensure(response.status_code == 200, "NEXT_UNAVAILABLE", "Next 服务不可用,未确认来源", 503) return response.json() except (httpx.HTTPError, ValueError) as exc: raise SampleFailure("NEXT_UNAVAILABLE", "无法核实 Next 身份或来源", 503) from exc def identity(request: Request) -> Identity: authorization = request.headers.get("authorization", "") project = request.headers.get("x-project-id", "") ensure(authorization.lower().startswith("bearer ") and bool(project), "AUTHENTICATION_REQUIRED", "需要 Bearer 和 X-Project-Id", 401) context = next_get(request, "/api/v1/agent-auth-context") try: user_id = str(UUID(context["user_id"])) project_id = str(UUID(context["project_id"])) ensure(project_id == str(UUID(project)), "PROJECT_MISMATCH", "项目身份不一致", 403) project_code = context["network"] ensure(isinstance(project_code, str) and bool(project_code.strip()), "INVALID_AUTH_CONTEXT", "Next 项目身份不完整", 502) permissions = context["permissions"] ensure(isinstance(permissions, list) and "simulation.view" in permissions, "PERMISSION_DENIED", "没有样板读取权限", 403) can_write = "simulation.run" in permissions ensure(request.method == "GET" or can_write, "PERMISSION_DENIED", "没有样板修改权限", 403) except (KeyError, TypeError, ValueError) as exc: raise SampleFailure("INVALID_AUTH_CONTEXT", "Next 身份响应不完整", 502) from exc return Identity(project_id, user_id, can_write, project_code)