From a6186244eef44d50706e78f02cba8402aabb2c28 Mon Sep 17 00:00:00 2001 From: bitloi <89318445+bitloi@users.noreply.github.com> Date: Thu, 21 May 2026 02:58:08 -0300 Subject: [PATCH] fix: handle missing SDK authorization headers (#15050) ### What problem does this PR solve? Closes #15048. Several SDK session routes in `api/apps/sdk/session.py` called `.split()` directly on `request.headers.get("Authorization")`. When clients omitted the header, the handlers raised `AttributeError` before returning the existing `Authorization is not valid!` response. This PR centralizes SDK Authorization parsing in a small helper and keeps the existing error response for missing, empty, or malformed headers. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) ### Tests - `ZHIPU_AI_API_KEY=dummy uv run --python 3.13 --group test pytest test/testcases/test_http_api/test_session_management/test_session_sdk_routes_unit.py::test_sdk_session_routes_missing_authorization_unit -q` - `uv run --python 3.13 --group test ruff check api/apps/sdk/session.py test/testcases/test_http_api/test_session_management/test_session_sdk_routes_unit.py` - `python3 -m py_compile api/apps/sdk/session.py test/testcases/test_http_api/test_session_management/test_session_sdk_routes_unit.py` - `git diff --check` --- api/apps/sdk/session.py | 52 +++++++++---------- .../test_session_sdk_routes_unit.py | 26 ++++++++++ 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/api/apps/sdk/session.py b/api/apps/sdk/session.py index 394ba71e9..ba65db5f3 100644 --- a/api/apps/sdk/session.py +++ b/api/apps/sdk/session.py @@ -53,6 +53,13 @@ from api.utils.reference_metadata_utils import ( logger = logging.getLogger(__name__) +def _get_sdk_authorization_token(): + token = request.headers.get("Authorization", "").split() + if len(token) != 2: + return None + return token[1] + + @token_required async def create_agent_session(tenant_id, agent_id): req = await get_request_json() @@ -148,10 +155,9 @@ async def delete_agent_session(tenant_id, agent_id): async def chatbot_completions(dialog_id): req = await get_request_json() - token = request.headers.get("Authorization").split() - if len(token) != 2: + token = _get_sdk_authorization_token() + if not token: return get_error_data_result(message='Authorization is not valid!') - token = token[1] objs = await thread_pool_exec(APIToken.query, beta=token) if not objs: return get_error_data_result(message='Authentication error: API key is invalid!"') @@ -223,10 +229,9 @@ async def chatbot_completions(dialog_id): @manager.route("/chatbots//info", methods=["GET"]) # noqa: F821 async def chatbots_inputs(dialog_id): - token = request.headers.get("Authorization").split() - if len(token) != 2: + token = _get_sdk_authorization_token() + if not token: return get_error_data_result(message='Authorization is not valid!') - token = token[1] objs = await thread_pool_exec(APIToken.query, beta=token) if not objs: return get_error_data_result(message='Authentication error: API key is invalid!"') @@ -261,10 +266,9 @@ async def chatbots_inputs(dialog_id): async def agent_bot_completions(agent_id): req = await get_request_json() - token = request.headers.get("Authorization").split() - if len(token) != 2: + token = _get_sdk_authorization_token() + if not token: return get_error_data_result(message='Authorization is not valid!') - token = token[1] objs = await thread_pool_exec(APIToken.query, beta=token) if not objs: return get_error_data_result(message='Authentication error: API key is invalid!"') @@ -347,10 +351,9 @@ async def agent_bot_completions(agent_id): @manager.route("/agentbots//inputs", methods=["GET"]) # noqa: F821 async def begin_inputs(agent_id): - token = request.headers.get("Authorization").split() - if len(token) != 2: + token = _get_sdk_authorization_token() + if not token: return get_error_data_result(message='Authorization is not valid!') - token = token[1] objs = await thread_pool_exec(APIToken.query, beta=token) if not objs: return get_error_data_result(message='Authentication error: API key is invalid!"') @@ -368,10 +371,9 @@ async def begin_inputs(agent_id): @manager.route("/searchbots/ask", methods=["POST"]) # noqa: F821 @validate_request("question", "kb_ids") async def ask_about_embedded(): - token = request.headers.get("Authorization").split() - if len(token) != 2: + token = _get_sdk_authorization_token() + if not token: return get_error_data_result(message='Authorization is not valid!') - token = token[1] objs = await thread_pool_exec(APIToken.query, beta=token) if not objs: return get_error_data_result(message='Authentication error: API key is invalid!"') @@ -407,10 +409,9 @@ async def ask_about_embedded(): @manager.route("/searchbots/retrieval_test", methods=["POST"]) # noqa: F821 @validate_request("kb_id", "question") async def retrieval_test_embedded(): - token = request.headers.get("Authorization").split() - if len(token) != 2: + token = _get_sdk_authorization_token() + if not token: return get_error_data_result(message='Authorization is not valid!') - token = token[1] objs = await thread_pool_exec(APIToken.query, beta=token) if not objs: return get_error_data_result(message='Authentication error: API key is invalid!"') @@ -563,10 +564,9 @@ async def retrieval_test_embedded(): @manager.route("/searchbots/related_questions", methods=["POST"]) # noqa: F821 @validate_request("question") async def related_questions_embedded(): - token = request.headers.get("Authorization").split() - if len(token) != 2: + token = _get_sdk_authorization_token() + if not token: return get_error_data_result(message='Authorization is not valid!') - token = token[1] objs = await thread_pool_exec(APIToken.query, beta=token) if not objs: return get_error_data_result(message='Authentication error: API key is invalid!"') @@ -611,10 +611,9 @@ Related search terms: @manager.route("/searchbots/detail", methods=["GET"]) # noqa: F821 async def detail_share_embedded(): - token = request.headers.get("Authorization").split() - if len(token) != 2: + token = _get_sdk_authorization_token() + if not token: return get_error_data_result(message='Authorization is not valid!') - token = token[1] objs = await thread_pool_exec(APIToken.query, beta=token) if not objs: return get_error_data_result(message='Authentication error: API key is invalid!"') @@ -643,10 +642,9 @@ async def detail_share_embedded(): @manager.route("/searchbots/mindmap", methods=["POST"]) # noqa: F821 @validate_request("question", "kb_ids") async def mindmap(): - token = request.headers.get("Authorization").split() - if len(token) != 2: + token = _get_sdk_authorization_token() + if not token: return get_error_data_result(message='Authorization is not valid!') - token = token[1] objs = await thread_pool_exec(APIToken.query, beta=token) if not objs: return get_error_data_result(message='Authentication error: API key is invalid!"') diff --git a/test/testcases/test_http_api/test_session_management/test_session_sdk_routes_unit.py b/test/testcases/test_http_api/test_session_management/test_session_sdk_routes_unit.py index 33f977a4c..66e5ed7c0 100644 --- a/test/testcases/test_http_api/test_session_management/test_session_sdk_routes_unit.py +++ b/test/testcases/test_http_api/test_session_management/test_session_sdk_routes_unit.py @@ -1405,6 +1405,32 @@ def test_delete_agent_session_error_matrix_unit(monkeypatch): assert res["data"]["errors"] == ["Duplicate session ids: ok"] +@pytest.mark.p2 +@pytest.mark.parametrize( + ("handler_name", "args"), + [ + ("chatbot_completions", ("dialog-1",)), + ("chatbots_inputs", ("dialog-1",)), + ("agent_bot_completions", ("agent-1",)), + ("begin_inputs", ("agent-1",)), + ("ask_about_embedded", ()), + ("retrieval_test_embedded", ()), + ("related_questions_embedded", ()), + ("detail_share_embedded", ()), + ("mindmap", ()), + ], +) +def test_sdk_session_routes_missing_authorization_unit(monkeypatch, handler_name, args): + module = _load_session_module(monkeypatch) + monkeypatch.setattr(module, "request", SimpleNamespace(headers={}, args={"search_id": "search-1"})) + monkeypatch.setattr(module, "get_request_json", lambda: _AwaitableValue({})) + + handler = inspect.unwrap(getattr(module, handler_name)) + res = _run(handler(*args)) + + assert res["message"] == "Authorization is not valid!" + + @pytest.mark.p2 def test_chatbot_routes_auth_stream_nonstream_unit(monkeypatch): module = _load_session_module(monkeypatch)