This is an automated email from the ASF dual-hosted git repository.

vatsrahul1001 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new b28681f154f Apply requires_access_event_log to GET /eventLogs list 
endpoint (#67185)
b28681f154f is described below

commit b28681f154f3e7d4f90055e59a640e5a6ffc3615
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Tue May 19 22:47:50 2026 +0200

    Apply requires_access_event_log to GET /eventLogs list endpoint (#67185)
    
    * Apply requires_access_event_log to GET /eventLogs list endpoint
    
    * Fail closed on non-integer event_log_id; fix list-endpoint test mock
    
    requires_access_event_log silently swallowed ValueError on non-integer
    event_log_id and fell through to the generic AUDIT_LOG check. Raise
    HTTPException(400) instead — matches the fail-closed pattern used by
    requires_access_backfill.
    
    Also fix test_requires_access_event_log_no_path_param_uses_generic_check:
    the test mocked request.path_params = {} but left request.query_params
    as an auto-created Mock attribute, whose .get("dag_id") returned a Mock
    (truthy non-None). requires_access_dag then resolved dag_id to that
    Mock and called is_authorized_dag with the wrong DagDetails. Mock both
    path_params and query_params as empty dicts.
    
    ---------
    
    Co-authored-by: Rahul Vats <[email protected]>
    Co-authored-by: vatsrahul1001 <[email protected]>
---
 .../core_api/routes/public/event_logs.py           |  4 +-
 .../src/airflow/api_fastapi/core_api/security.py   | 14 ++++---
 .../unit/api_fastapi/core_api/test_security.py     | 48 ++++++++++++++++++++++
 3 files changed, 57 insertions(+), 9 deletions(-)

diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/event_logs.py 
b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/event_logs.py
index c4fb56b5bb5..5088fb6f6b9 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/event_logs.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/event_logs.py
@@ -46,9 +46,7 @@ from airflow.api_fastapi.core_api.datamodels.event_logs 
import (
 )
 from airflow.api_fastapi.core_api.openapi.exceptions import 
create_openapi_http_exception_doc
 from airflow.api_fastapi.core_api.security import (
-    DagAccessEntity,
     ReadableEventLogsFilterDep,
-    requires_access_dag,
     requires_access_event_log,
 )
 from airflow.models import Log
@@ -75,7 +73,7 @@ def get_event_log(
 
 @event_logs_router.get(
     "",
-    dependencies=[Depends(requires_access_dag("GET", 
DagAccessEntity.AUDIT_LOG))],
+    dependencies=[Depends(requires_access_event_log("GET"))],
 )
 def get_event_logs(
     limit: QueryLimit,
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/security.py 
b/airflow-core/src/airflow/api_fastapi/core_api/security.py
index 5ea8821ccea..a54425326d3 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/security.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/security.py
@@ -369,12 +369,14 @@ def requires_access_event_log(
         dag_id = None
 
         event_log_id_raw = request.path_params.get("event_log_id")
-        try:
-            event_log_id = int(event_log_id_raw) if event_log_id_raw is not 
None else None
-        except ValueError:
-            event_log_id = None
-
-        if event_log_id is not None:
+        if event_log_id_raw is not None:
+            try:
+                event_log_id = int(event_log_id_raw)
+            except ValueError:
+                raise HTTPException(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    detail="'event_log_id' must be an integer",
+                )
             dag_id = session.scalar(select(Log.dag_id).where(Log.id == 
event_log_id))
 
         requires_access_dag(method, DagAccessEntity.AUDIT_LOG, dag_id)(
diff --git a/airflow-core/tests/unit/api_fastapi/core_api/test_security.py 
b/airflow-core/tests/unit/api_fastapi/core_api/test_security.py
index b19f774f28a..ef8a3972909 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/test_security.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/test_security.py
@@ -501,6 +501,54 @@ class TestFastApiSecurity:
         )
         mock_get_team_name.assert_not_called()
 
+    @pytest.mark.db_test
+    @pytest.mark.parametrize("bad_event_log_id", ["abc", "1.5", "1,2", ""])
+    @patch("airflow.api_fastapi.core_api.security.requires_access_dag")
+    async def test_requires_access_event_log_non_integer_id_returns_400(
+        self, mock_requires_access_dag, bad_event_log_id
+    ):
+        """Non-integer event_log_id in the path must be rejected with 400 
before authz."""
+        request = Mock()
+        request.path_params = {"event_log_id": bad_event_log_id}
+        user = Mock()
+        session = Mock()
+
+        with pytest.raises(HTTPException) as exc_info:
+            await requires_access_event_log("GET")(request, user, session)
+
+        assert exc_info.value.status_code == 400
+        assert "event_log_id" in exc_info.value.detail
+        mock_requires_access_dag.assert_not_called()
+        session.scalar.assert_not_called()
+
+    @pytest.mark.db_test
+    @patch.object(DagModel, "get_team_name")
+    @patch("airflow.api_fastapi.core_api.security.get_auth_manager")
+    async def test_requires_access_event_log_no_path_param_uses_generic_check(
+        self, mock_get_auth_manager, mock_get_team_name
+    ):
+        """When called on the list endpoint (no event_log_id), the generic 
AUDIT_LOG check applies."""
+        auth_manager = Mock()
+        auth_manager.is_authorized_dag.return_value = True
+        mock_get_auth_manager.return_value = auth_manager
+
+        session = Mock()
+        request = Mock()
+        request.path_params = {}
+        request.query_params = {}
+        user = Mock()
+
+        await requires_access_event_log("GET")(request, user, session)
+
+        auth_manager.is_authorized_dag.assert_called_once_with(
+            method="GET",
+            access_entity=DagAccessEntity.AUDIT_LOG,
+            details=DagDetails(id=None, team_name=None),
+            user=user,
+        )
+        session.scalar.assert_not_called()
+        mock_get_team_name.assert_not_called()
+
     @pytest.mark.parametrize(
         ("url", "expected_is_safe"),
         [

Reply via email to