From e1a4fcbde0bb3cfcf6bee69576b5e5427a544b71 Mon Sep 17 00:00:00 2001 From: Deepthi Rao Date: Fri, 12 Jun 2026 13:24:48 -0400 Subject: [PATCH] fix(authz): check authorization through gateway --- .../domain/services/authorization_service.py | 30 ------------- .../api/agents/test_agents_auth_api.py | 9 ++-- .../test_authorization_service_cache.py | 45 +++++++++++++++++++ 3 files changed, 51 insertions(+), 33 deletions(-) create mode 100644 agentex/tests/unit/services/test_authorization_service_cache.py diff --git a/agentex/src/domain/services/authorization_service.py b/agentex/src/domain/services/authorization_service.py index 936492f2..33cceba1 100644 --- a/agentex/src/domain/services/authorization_service.py +++ b/agentex/src/domain/services/authorization_service.py @@ -6,7 +6,6 @@ from src.adapters.authorization.adapter_agentex_authz_proxy import ( DAgentexAuthorization, ) -from src.api.authentication_cache import get_auth_cache from src.api.authentication_middleware import DAuthorizationEnabled from src.api.schemas.authorization_types import ( AgentexResource, @@ -107,26 +106,6 @@ async def check( else self.principal_context ) - # Try to get cached result first - auth_cache = await get_auth_cache() - cached_result = await auth_cache.get_authorization_check( - resource_type=str(resource.type), - resource_selector=resource.selector, - operation=str(operation), - principal_context=effective_principal, - ) - - if cached_result is not None: - logger.info( - "[authorization_service] Using cached result for %s permission on %s:%s: %s", - operation, - resource.type, - resource.selector, - "allowed" if cached_result else "denied", - ) - return cached_result - - # Not in cache, perform actual check logger.info( "[authorization_service] Checking %s permission on %s:%s", operation, @@ -139,15 +118,6 @@ async def check( operation, ) - # Cache the result - await auth_cache.set_authorization_check( - resource_type=str(resource.type), - resource_selector=resource.selector, - operation=str(operation), - principal_context=effective_principal, - allowed=result, - ) - logger.info( f"Authorization check for {operation} on {resource.type}:{resource.selector}: {'allowed' if result else 'denied'}" ) diff --git a/agentex/tests/integration/api/agents/test_agents_auth_api.py b/agentex/tests/integration/api/agents/test_agents_auth_api.py index 676ddd93..7266b6c2 100644 --- a/agentex/tests/integration/api/agents/test_agents_auth_api.py +++ b/agentex/tests/integration/api/agents/test_agents_auth_api.py @@ -165,15 +165,18 @@ async def test_agent_check( assert authz_data["operation"] == "read" assert authz_data["principal"] == MOCK_PRINCIPAL_CONTEXT - # Second call will use auth cache and not make a request to the authn service + # Second call still uses the authn cache, but authorization checks go + # directly to Spark so share/revoke changes are visible immediately. post_with_error_handling_mock.reset_mock() response = await isolated_client.get("/agents/name/test-authorized-agent") assert response.status_code == 200 agent = response.json() assert agent["id"] == test_authorized_agent.id - # No request to the authz service thanks to auth cache - assert post_with_error_handling_mock.call_count == 0 + assert post_with_error_handling_mock.call_count == 1 + assert ( + post_with_error_handling_mock.call_args_list[0][0][1] == "/v1/authz/check" + ) @pytest.mark.asyncio @patch( diff --git a/agentex/tests/unit/services/test_authorization_service_cache.py b/agentex/tests/unit/services/test_authorization_service_cache.py new file mode 100644 index 00000000..b597f245 --- /dev/null +++ b/agentex/tests/unit/services/test_authorization_service_cache.py @@ -0,0 +1,45 @@ +from types import SimpleNamespace +from unittest.mock import AsyncMock + +import pytest +from src.api.schemas.authorization_types import AgentexResource, AuthorizedOperationType +from src.domain.services.authorization_service import AuthorizationService + + +def _request_with_principal(principal_context): + return SimpleNamespace( + state=SimpleNamespace( + principal_context=principal_context, + agent_identity=None, + ) + ) + + +def _service(principal_context, gateway): + return AuthorizationService( + enabled=True, + gateway=gateway, + request=_request_with_principal(principal_context), + ) + + +@pytest.mark.unit +@pytest.mark.asyncio +@pytest.mark.parametrize( + "resource", + [ + AgentexResource.agent("agent-1"), + AgentexResource.task("task-1"), + AgentexResource.api_key("api-key-1"), + AgentexResource.schedule("agent-1/schedule-1"), + ], +) +async def test_authorization_checks_call_gateway_each_time(resource): + gateway = AsyncMock() + gateway.check.return_value = True + service = _service({"user_id": "user-1", "account_id": "acct-1"}, gateway) + + assert await service.check(resource, AuthorizedOperationType.read) is True + assert await service.check(resource, AuthorizedOperationType.read) is True + + assert gateway.check.await_count == 2