Skip to content

Commit 7cde398

Browse files
authored
fix: Add validation to ensure added auth token getters are used by the tool (#220)
* fix: Add validation to ensure added auth token getters are used by the tool Previously, the `ToolboxTool.add_auth_token_getters` method only validated against existing registered getters or conflicts with client headers. It did not verify if *all* the auth token getters provided were actually used or required by the specific tool instance they were being added to. This PR enhances the validation in `add_auth_token_getters`. It now leverages the `used_auth_token_getters` information returned by the existing `identify_required_authn_params` call. This allows the method to confirm that every getter passed in is genuinely required by the tool, raising an error if any are unused. This ensures that only relevant auth token getters are attempted to be registered for a tool, preventing misconfigurations and human errors. > [!NOTE] > This validation aligns with the existing validation logic already present in the `ToolboxClient.load_tool` method, promoting a consistent approach to handling auth token getter requirements across the codebase. * docs: Improve helper comment * chore: Add unit test cases * chore: Delint
1 parent 2dad7c8 commit 7cde398

File tree

3 files changed

+74
-3
lines changed

3 files changed

+74
-3
lines changed

packages/toolbox-core/src/toolbox_core/tool.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,8 @@ def add_auth_token_getters(
309309

310310
new_getters = dict(self.__auth_service_token_getters, **auth_token_getters)
311311

312-
# find the updated requirements
312+
# find the updated required authn params, authz tokens and the auth
313+
# token getters used
313314
new_req_authn_params, new_req_authz_tokens, used_auth_token_getters = (
314315
identify_auth_requirements(
315316
self.__required_authn_params,
@@ -318,7 +319,12 @@ def add_auth_token_getters(
318319
)
319320
)
320321

321-
# TODO: Add validation for used_auth_token_getters
322+
# ensure no auth token getter provided remains unused
323+
unused_auth = set(incoming_services) - used_auth_token_getters
324+
if unused_auth:
325+
raise ValueError(
326+
f"Authentication source(s) `{', '.join(unused_auth)}` unused by tool `{self.__name__}`."
327+
)
322328

323329
return self.__copy(
324330
# create a read-only map for updated getters, params and tokens that are still required

packages/toolbox-core/tests/test_client.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,34 @@ async def test_add_auth_token_getters_duplicate_fail(self, tool_name, client):
371371
):
372372
authed_tool.add_auth_token_getters({AUTH_SERVICE: {}})
373373

374+
@pytest.mark.asyncio
375+
async def test_add_auth_token_getters_missing_fail(self, tool_name, client):
376+
"""
377+
Tests that adding a missing auth token getter raises ValueError.
378+
"""
379+
AUTH_SERVICE = "xmy-auth-service"
380+
381+
tool = await client.load_tool(tool_name)
382+
383+
with pytest.raises(
384+
ValueError,
385+
match=f"Authentication source\(s\) \`{AUTH_SERVICE}\` unused by tool \`{tool_name}\`.",
386+
):
387+
tool.add_auth_token_getters({AUTH_SERVICE: {}})
388+
389+
@pytest.mark.asyncio
390+
async def test_constructor_getters_missing_fail(self, tool_name, client):
391+
"""
392+
Tests that adding a missing auth token getter raises ValueError.
393+
"""
394+
AUTH_SERVICE = "xmy-auth-service"
395+
396+
with pytest.raises(
397+
ValueError,
398+
match=f"Validation failed for tool '{tool_name}': unused auth tokens: {AUTH_SERVICE}.",
399+
):
400+
await client.load_tool(tool_name, auth_token_getters={AUTH_SERVICE: {}})
401+
374402

375403
class TestBoundParameter:
376404

packages/toolbox-core/tests/test_tool.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
15+
1416
import inspect
15-
from typing import AsyncGenerator, Callable
17+
from typing import AsyncGenerator, Callable, Mapping
1618
from unittest.mock import AsyncMock, Mock
1719

1820
import pytest
@@ -92,6 +94,12 @@ def auth_header_key() -> str:
9294
return "test-auth_token"
9395

9496

97+
@pytest.fixture
98+
def unused_auth_getters() -> dict[str, Callable[[], str]]:
99+
"""Provides an auth getter for a service not required by sample_tool."""
100+
return {"unused-auth-service": lambda: "unused-token-value"}
101+
102+
95103
def test_create_func_docstring_one_param_real_schema():
96104
"""
97105
Tests create_func_docstring with one real ParameterSchema instance.
@@ -432,3 +440,32 @@ def test_tool_add_auth_token_getters_conflict_with_existing_client_header(
432440

433441
with pytest.raises(ValueError, match=expected_error_message):
434442
tool_instance.add_auth_token_getters(new_auth_getters_causing_conflict)
443+
444+
445+
def test_add_auth_token_getters_unused_token(
446+
http_session: ClientSession,
447+
sample_tool_params: list[ParameterSchema],
448+
sample_tool_description: str,
449+
unused_auth_getters: Mapping[str, Callable[[], str]],
450+
):
451+
"""
452+
Tests ValueError when add_auth_token_getters is called with a getter for
453+
an unused authentication service.
454+
"""
455+
tool_instance = ToolboxTool(
456+
session=http_session,
457+
base_url=TEST_BASE_URL,
458+
name=TEST_TOOL_NAME,
459+
description=sample_tool_description,
460+
params=sample_tool_params,
461+
required_authn_params={},
462+
required_authz_tokens=[],
463+
auth_service_token_getters={},
464+
bound_params={},
465+
client_headers={},
466+
)
467+
468+
expected_error_message = "Authentication source\(s\) \`unused-auth-service\` unused by tool \`sample_tool\`."
469+
470+
with pytest.raises(ValueError, match=expected_error_message):
471+
tool_instance.add_auth_token_getters(unused_auth_getters)

0 commit comments

Comments
 (0)