deer-flow/backend/tests/blocking_io/test_sandbox_authorization.py
PeaceMaker-best 137a3cb60d
fix(authz): recheck policy before sandbox reuse (#5006)
* fix(authz): recheck policy before sandbox reuse

* fix(authz): avoid duplicate async sandbox checks

* fix(authz): scope sandbox decision across middleware

* fix(authz): construct async providers on the event loop

* test(authz): avoid cold imports under Blockbuster

---------

Co-authored-by: 嗜鵼 <hy2010hy2010@qq.com>
Co-authored-by: PeaceMaker-best <221849497+PeaceMaker-best@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-08-30 15:29:51 +08:00

65 lines
2.4 KiB
Python

"""Sandbox authorization resolution must stay off the async event loop."""
from __future__ import annotations
import asyncio
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import MagicMock
import pytest
from deerflow.authz import sandbox_authz
from deerflow.config.app_config import AppConfig
from deerflow.config.authorization_config import AuthorizationConfig, AuthorizationProviderConfig
from deerflow.config.model_config import ModelConfig
from deerflow.config.sandbox_config import SandboxConfig
from deerflow.sandbox import tools as sandbox_tools
pytestmark = pytest.mark.asyncio
async def test_reused_async_sandbox_offloads_config_and_provider_resolution(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
"""Config hashing and class discovery stay off-loop; construction does not."""
probe = tmp_path / "sandbox-authz-probe"
await asyncio.to_thread(probe.write_text, "probe", encoding="utf-8")
app_config = AppConfig(
models=[ModelConfig(name="gpt-4", model="gpt-4", use="langchain_openai:ChatOpenAI")],
sandbox=SandboxConfig(use="deerflow.sandbox.local:LocalSandboxProvider"),
authorization=AuthorizationConfig(
enabled=True,
fail_closed=True,
default_role="user",
provider=AuthorizationProviderConfig(
use="deerflow.authz.rbac:RbacAuthorizationProvider",
config={"roles": {"user": {"sandbox": {"allow": "*"}}}},
),
),
)
def blocking_config_load():
probe.read_text(encoding="utf-8")
return app_config
discover_provider = sandbox_authz.resolve_authorization_provider_spec
def blocking_provider_discovery(config):
probe.read_text(encoding="utf-8")
return discover_provider(config)
monkeypatch.setattr(sandbox_authz, "safe_app_config", blocking_config_load)
monkeypatch.setattr(sandbox_authz, "resolve_authorization_provider_spec", blocking_provider_discovery)
sandbox = MagicMock()
sandbox_provider = MagicMock()
sandbox_provider.get.return_value = sandbox
monkeypatch.setattr(sandbox_tools, "get_sandbox_provider", lambda: sandbox_provider)
runtime = SimpleNamespace(
state={"sandbox": {"sandbox_id": "sbx-existing"}},
context={"thread_id": "t1", "user_id": "u1", "user_role": "user"},
config=None,
)
assert await sandbox_tools.ensure_sandbox_initialized_async(runtime) is sandbox