mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-06-10 09:25:57 +00:00
fix(mcp): add auth interceptor with channel user_id and keep header propagation to mcp tools (#3294)
* 修复channel中的user_id传递到interceptor中的bug, mcp可通过header传递user_id到mcp工具 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(channel,mcp,gateway): normalize channel user_id and add regression tests Normalize external channel user ids into filesystem-safe runtime context while preserving raw channel_user_id, and document gateway user_id propagation semantics. Add regression coverage for channel user_id context mapping, gateway user_id precedence/internal-role behavior, and MCP interceptor header forwarding via meta.headers. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(auth,mcp): harden user id normalization and header handling Increase sanitized user-id digest suffix to 16 hex chars, replace internal system role magic string with a shared constant, and harden MCP header forwarding with Mapping type checks. Add regression tests for empty channel user_id handling, unsupported header types, and updated digest length behavior. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: zhongli <335302680@qq.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -431,6 +431,49 @@ def test_inject_authenticated_user_context_overrides_client_user_id():
|
||||
assert config["context"]["user_id"] == "auth-user-42"
|
||||
|
||||
|
||||
def test_merge_run_context_overrides_propagates_user_id():
|
||||
"""Regression for PR #3294: ``user_id`` from ``body.context`` must land in
|
||||
``config['context']`` so non-web callers (e.g. IM channels) keep their identity
|
||||
on ``ToolRuntime.context``.
|
||||
"""
|
||||
from app.gateway.services import build_run_config, merge_run_context_overrides
|
||||
|
||||
config = build_run_config("thread-1", None, None)
|
||||
merge_run_context_overrides(config, {"user_id": "channel-user-7"})
|
||||
|
||||
assert config["context"]["user_id"] == "channel-user-7"
|
||||
|
||||
|
||||
def test_merge_run_context_overrides_does_not_clobber_existing_user_id():
|
||||
"""``merge_run_context_overrides`` must not override an already-stamped
|
||||
authenticated ``context.user_id`` with the client-supplied value.
|
||||
"""
|
||||
from app.gateway.services import build_run_config, merge_run_context_overrides
|
||||
|
||||
config = build_run_config("thread-1", {"context": {"user_id": "auth-user-42"}}, None)
|
||||
merge_run_context_overrides(config, {"user_id": "spoofed-client"})
|
||||
|
||||
assert config["context"]["user_id"] == "auth-user-42"
|
||||
|
||||
|
||||
def test_inject_authenticated_user_context_skips_internal_role():
|
||||
"""Regression for PR #3294: internal system-role callers must not overwrite an
|
||||
already-present ``context.user_id`` (e.g. a channel-supplied identity), so the
|
||||
real end user keeps owning the per-user storage bucket.
|
||||
"""
|
||||
from types import SimpleNamespace
|
||||
|
||||
from app.gateway.services import build_run_config, inject_authenticated_user_context
|
||||
|
||||
config = build_run_config("thread-1", None, None)
|
||||
config["context"] = {"user_id": "channel-user-7"}
|
||||
request = SimpleNamespace(state=SimpleNamespace(user=SimpleNamespace(id="internal-bot", system_role="internal")))
|
||||
|
||||
inject_authenticated_user_context(config, request)
|
||||
|
||||
assert config["context"]["user_id"] == "channel-user-7"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# build_run_config — context / configurable precedence (LangGraph >= 0.6.0)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user