diff --git a/horizon/enforcer/api.py b/horizon/enforcer/api.py index 49f43092..4dbe6507 100644 --- a/horizon/enforcer/api.py +++ b/horizon/enforcer/api.py @@ -7,6 +7,7 @@ from fastapi import APIRouter, Depends, Header from fastapi import HTTPException from fastapi import Request, Response, status +from fastapi.encoders import jsonable_encoder from opal_client.config import opal_client_config from opal_client.logger import logger from opal_client.policy_store.base_policy_store_client import BasePolicyStoreClient @@ -307,7 +308,7 @@ async def conditional_is_allowed( async def _is_allowed_data_manager( - query: BaseSchema | None, + query: BaseSchema | list[BaseSchema] | None, request: Request, *, path: str = "/check", @@ -316,9 +317,10 @@ async def _is_allowed_data_manager( ): headers = transform_headers(request) url = f"{sidecar_config.DATA_MANAGER_SERVICE_URL}/v1/authz{path}" - payload = None if query is None else {"input": query.dict()} + payload = None if query is None else {"input": jsonable_encoder(query)} exc = None - _set_use_debugger(payload) + if query is not None and isinstance(query, dict): + _set_use_debugger(payload) try: logger.info(f"calling Data Manager at '{url}' with input: {payload}") async with aiohttp.ClientSession() as session: @@ -603,25 +605,19 @@ async def is_allowed_bulk( queries: list[AuthorizationQuery], x_permit_sdk_language: Optional[str] = Depends(notify_seen_sdk), ): - bulk_query = BulkAuthorizationQuery(checks=queries) if sidecar_config.ENABLE_EXTERNAL_DATA_MANAGER: response = await _is_allowed_data_manager( - bulk_query, request, path="/check/bulk" + queries, request, path="/check/bulk" ) raw_result = json.loads(response.body) - log_query_result(bulk_query, response, is_inner=True) else: + bulk_query = BulkAuthorizationQuery(checks=queries) response = await _is_allowed(bulk_query, request, BULK_POLICY_PACKAGE) - raw_result = json.loads(response.body).get("result", {}) + raw_result = json.loads(response.body).get("result", {}).get("allow", []) log_query_result(bulk_query, response) try: - processed_query = ( - get_v1_processed_query(raw_result) - or get_v2_processed_query(raw_result) - or {} - ) result = BulkAuthorizationResult( - allow=raw_result.get("allow", []), + allow=raw_result, ) except Exception: result = BulkAuthorizationResult( diff --git a/horizon/local/api.py b/horizon/local/api.py index a8aaa881..3e57dfd6 100644 --- a/horizon/local/api.py +++ b/horizon/local/api.py @@ -20,6 +20,7 @@ ListRoleAssignmentsPDPBody, WrappedResponse, ListRoleAssignmentsPagination, + RoleAssignmentFactDBFact, ) @@ -170,7 +171,10 @@ async def legacy_list_role_assignments() -> list[RoleAssignment]: return await legacy_list_role_assignments() else: res = await policy_store.list_facts_by_type("role_assignments") - return parse_obj_as(list[RoleAssignment], await res.json()) + res_json = parse_obj_as( + list[RoleAssignmentFactDBFact], await res.json() + ) + return [fact.into_role_assignment() for fact in res_json] else: return await legacy_list_role_assignments() diff --git a/horizon/local/schemas.py b/horizon/local/schemas.py index 81bf7a3e..0fb9989e 100644 --- a/horizon/local/schemas.py +++ b/horizon/local/schemas.py @@ -84,3 +84,49 @@ class Config: class WrappedResponse(BaseSchema): result: list[RoleAssignment] + + +class FactDBFact(BaseSchema): + type: str + attributes: dict[str, Any] + + +class RoleAssignmentFactDBFact(FactDBFact): + @property + def user(self) -> str: + return self.attributes.get("actor", "").removeprefix("user:") + + @property + def role(self) -> str: + return self.attributes.get("role", "") + + @property + def tenant(self) -> str: + return self.attributes.get("tenant", "") + + @property + def resource_instance(self) -> str: + return self.attributes.get("resource", "") + + def into_role_assignment(self) -> RoleAssignment: + return RoleAssignment( + user=self.user, + role=self.role, + tenant=self.tenant, + resource_instance=self.resource_instance, + ) + + class Config: + schema_extra = { + "example": { + "type": "role_assignments", + "attributes": { + "actor": "user:author-user", + "id": "user:author-user-author-document:doc-1", + "last_modified": "2024-09-23 09:10:10 +0000 UTC", + "resource": "document:doc-1", + "role": "author", + "tenant": "default", + }, + } + } diff --git a/horizon/tests/test_local_api.py b/horizon/tests/test_local_api.py index e0082760..08a53155 100644 --- a/horizon/tests/test_local_api.py +++ b/horizon/tests/test_local_api.py @@ -142,10 +142,14 @@ async def test_list_role_assignments_external_data_store() -> None: repeat=True, payload=[ { - "user": "user1", - "role": "role1", - "tenant": "tenant1", - "resource_instance": "resource_instance1", + "type": "role_assignment", + "attributes": { + "actor": "user:user1", + "role": "role1", + "tenant": "tenant1", + "resource": "resource_instance1", + "id": "user:user1-role1-resource_instance1", + }, } ], )