fix(agent): authenticate agent control plane (#40737)

This commit is contained in:
WH-2099
2026-08-14 04:32:09 +00:00
committed by GitHub
parent e2852524be
commit 3cd23d4ef8
16 changed files with 131 additions and 31 deletions
+1 -1
View File
@@ -703,7 +703,7 @@ INNER_API_KEY_FOR_PLUGIN=QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y
# Dify Agent backend
AGENT_BACKEND_BASE_URL=http://localhost:5050
# Bearer token sent to the Agent backend /runs API. Must match DIFY_AGENT_API_TOKEN on the server side.
# Bearer token sent to the Agent backend control-plane API. Must match DIFY_AGENT_API_TOKEN on the server side.
AGENT_BACKEND_API_TOKEN=dify-agent-run-token-for-dev-only
AGENT_BACKEND_STREAM_READ_TIMEOUT_SECONDS=30
AGENT_BACKEND_STREAM_MAX_RECONNECTS=3
+11 -4
View File
@@ -8,6 +8,12 @@ from clients.agent_backend.client import AgentBackendRunClient, DifyAgentBackend
from clients.agent_backend.fake_client import FakeAgentBackendRunClient, FakeAgentBackendScenario
def create_agent_backend_client(*, base_url: str, api_token: str | None = None, stream_timeout: float = 30) -> Client:
api_token = api_token.strip() if api_token else None
headers = {"Authorization": f"Bearer {api_token}"} if api_token else None
return Client(base_url=base_url, stream_timeout=stream_timeout, headers=headers)
def create_agent_backend_run_client(
*,
base_url: str | None = None,
@@ -23,11 +29,12 @@ def create_agent_backend_run_client(
return FakeAgentBackendRunClient(scenario=FakeAgentBackendScenario(fake_scenario))
if base_url is None:
raise ValueError("base_url is required when creating a real Agent backend client")
headers: dict[str, str] = {}
if api_token:
headers["Authorization"] = f"Bearer {api_token}"
return DifyAgentBackendRunClient(
Client(base_url=base_url, stream_timeout=stream_read_timeout_seconds, headers=headers),
create_agent_backend_client(
base_url=base_url,
api_token=api_token,
stream_timeout=stream_read_timeout_seconds,
),
stream_max_reconnects=stream_max_reconnects,
stream_timeout_seconds=stream_run_timeout_seconds,
)
+1 -1
View File
@@ -13,7 +13,7 @@ class AgentBackendConfig(BaseSettings):
)
AGENT_BACKEND_API_TOKEN: str | None = Field(
description="Bearer token for authenticating with the Agent backend /runs API.",
description="Bearer token for authenticating with the Agent backend control-plane API.",
default=None,
)
+5 -1
View File
@@ -7,6 +7,7 @@ from dify_agent.protocol import CreateHomeSnapshotFromBindingRequest
from sqlalchemy import select
from sqlalchemy.orm import Session
from clients.agent_backend.factory import create_agent_backend_client
from configs import dify_config
from core.db.session_factory import session_factory
from libs.datetime_utils import naive_utc_now
@@ -159,7 +160,10 @@ class AgentHomeSnapshotService:
base_url = dify_config.AGENT_BACKEND_BASE_URL
if not base_url:
raise AgentHomeSnapshotUnavailableError("Dify Agent backend is required for Home Snapshot operations")
return Client(base_url=base_url)
return create_agent_backend_client(
base_url=base_url,
api_token=dify_config.AGENT_BACKEND_API_TOKEN,
)
def validate_home_snapshot_binding(*, session: Session, agent: Agent, home_snapshot_id: str | None) -> None:
+5 -1
View File
@@ -16,6 +16,7 @@ from dify_agent.protocol import CreateExecutionBindingRequest, DestroyExecutionB
from sqlalchemy import select
from sqlalchemy.orm import Session
from clients.agent_backend.factory import create_agent_backend_client
from configs import dify_config
from core.db.session_factory import session_factory
from libs.datetime_utils import naive_utc_now
@@ -426,7 +427,10 @@ class AgentWorkspaceService:
base_url = dify_config.AGENT_BACKEND_BASE_URL
if not base_url:
raise AgentWorkspaceError("Dify Agent backend is required for Workspace operations")
return Client(base_url=base_url)
return create_agent_backend_client(
base_url=base_url,
api_token=dify_config.AGENT_BACKEND_API_TOKEN,
)
__all__ = [
+5 -1
View File
@@ -21,6 +21,7 @@ from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.orm import Session
from clients.agent_backend.factory import create_agent_backend_client
from configs import dify_config
from core.db.session_factory import session_factory
from core.tools.signature import bind_file_uri
@@ -472,7 +473,10 @@ def _default_client_factory() -> Client:
"the Binding file inspector is not available (Agent backend not configured)",
status_code=503,
)
return Client(base_url=base_url)
return create_agent_backend_client(
base_url=base_url,
api_token=dify_config.AGENT_BACKEND_API_TOKEN,
)
__all__ = [
@@ -0,0 +1,58 @@
from collections.abc import Callable
from types import ModuleType
from unittest.mock import MagicMock, patch
import pytest
from dify_agent.client import Client
from clients.agent_backend.factory import create_agent_backend_client
from configs import dify_config
from services import agent_app_sandbox_service
from services.agent import home_snapshot_service, workspace_service
@pytest.mark.parametrize(
("api_token", "headers"),
[
("secret-token", {"Authorization": "Bearer secret-token"}),
(" secret-token ", {"Authorization": "Bearer secret-token"}),
(" ", None),
(None, None),
],
)
@patch("clients.agent_backend.factory.Client")
def test_create_agent_backend_client_forwards_authentication(
client_cls: MagicMock,
api_token: str | None,
headers: dict[str, str] | None,
) -> None:
create_agent_backend_client(base_url="http://agent-backend", api_token=api_token)
client_cls.assert_called_once_with(
base_url="http://agent-backend",
stream_timeout=30,
headers=headers,
)
@pytest.mark.parametrize(
("factory", "module"),
[
(home_snapshot_service.AgentHomeSnapshotService._client, home_snapshot_service),
(workspace_service.AgentWorkspaceService._client, workspace_service),
(agent_app_sandbox_service._default_client_factory, agent_app_sandbox_service),
],
)
def test_default_agent_backend_clients_forward_authentication(
monkeypatch: pytest.MonkeyPatch,
factory: Callable[[], Client],
module: ModuleType,
) -> None:
monkeypatch.setattr(dify_config, "AGENT_BACKEND_BASE_URL", "http://agent-backend")
monkeypatch.setattr(dify_config, "AGENT_BACKEND_API_TOKEN", "secret-token")
create_client = MagicMock()
monkeypatch.setattr(module, "create_agent_backend_client", create_client)
factory()
create_client.assert_called_once_with(base_url="http://agent-backend", api_token="secret-token")