mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-11-12 14:00:35 +00:00
* feat(weknora): allow opt-in shared authenticated retrieval * docs(weknora): clarify shared retrieval identity requirements * docs(weknora): clarify durable batch authentication limits * docs(gateway): keep agent guidance within hard size limit --------- Co-authored-by: Totoro <msy626836554@gmail.com> Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
79 lines
3.2 KiB
Python
79 lines
3.2 KiB
Python
"""Authentication provenance must remain separate from the storage user ID."""
|
|
|
|
import asyncio
|
|
from contextvars import Context
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.mark.no_auto_user
|
|
def test_user_context_or_client_metadata_is_not_authentication():
|
|
from deerflow.runtime.user_context import get_authenticated_user_id, reset_current_user, set_current_user
|
|
|
|
assert get_authenticated_user_id() is None
|
|
token = set_current_user(SimpleNamespace(id="user", authenticated=True))
|
|
try:
|
|
assert get_authenticated_user_id() is None
|
|
finally:
|
|
reset_current_user(token)
|
|
|
|
|
|
def test_verified_identity_can_be_cleared_and_restored():
|
|
from deerflow.runtime.user_context import bind_authenticated_user, get_authenticated_user_id
|
|
|
|
with bind_authenticated_user("alice"):
|
|
assert get_authenticated_user_id() == "alice"
|
|
with bind_authenticated_user(None):
|
|
assert get_authenticated_user_id() is None
|
|
assert get_authenticated_user_id() == "alice"
|
|
with pytest.raises(RuntimeError), bind_authenticated_user("bob"):
|
|
raise RuntimeError("cancelled request")
|
|
assert get_authenticated_user_id() == "alice"
|
|
assert get_authenticated_user_id() is None
|
|
|
|
|
|
@pytest.mark.parametrize("invalid", ["", " ", " alice ", True, 1])
|
|
def test_verified_identity_rejects_invalid_host_bindings(invalid):
|
|
from deerflow.runtime.user_context import bind_authenticated_user
|
|
|
|
with pytest.raises(ValueError), bind_authenticated_user(invalid):
|
|
pass
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_verified_identity_survives_child_tasks_and_threads_without_cross_user_leaks():
|
|
from deerflow.runtime.user_context import bind_authenticated_user, get_authenticated_user_id
|
|
|
|
ready = asyncio.Event()
|
|
|
|
async def child(user):
|
|
await ready.wait()
|
|
assert get_authenticated_user_id() == user
|
|
return await asyncio.to_thread(get_authenticated_user_id)
|
|
|
|
with bind_authenticated_user("alice"):
|
|
alice = asyncio.create_task(child("alice"))
|
|
with bind_authenticated_user("bob"):
|
|
bob = asyncio.create_task(child("bob"))
|
|
assert get_authenticated_user_id() is None
|
|
ready.set()
|
|
assert await asyncio.gather(alice, bob) == ["alice", "bob"]
|
|
assert Context().run(get_authenticated_user_id) is None
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_search_receives_host_proof_separately_from_its_user_id():
|
|
from unittest.mock import AsyncMock
|
|
|
|
from deerflow.knowledge import KnowledgeCapabilities, RetrievalResult
|
|
from deerflow.knowledge.providers import search_provider
|
|
from deerflow.runtime.user_context import bind_authenticated_user
|
|
|
|
provider = SimpleNamespace(capabilities=KnowledgeCapabilities(), search_scoped=AsyncMock(return_value=RetrievalResult()))
|
|
await search_provider(provider, "query", scope=None, user_id="alice", agent_name="lead_agent")
|
|
assert provider.search_scoped.await_args.kwargs["context"].authenticated_user_id is None
|
|
with bind_authenticated_user("alice"):
|
|
await search_provider(provider, "query", scope=None, user_id="alice", agent_name="researcher")
|
|
assert provider.search_scoped.await_args.kwargs["context"].authenticated_user_id == "alice"
|