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

pierrejeambrun 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 c064739d3a7 Fix permissions on get_event_logs endpoint (#60936)
c064739d3a7 is described below

commit c064739d3a7686834eca6773ed6497e423589a9d
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Fri Jan 23 01:04:42 2026 +0100

    Fix permissions on get_event_logs endpoint (#60936)
    
    * Fix permissions on get_event_logs endpoint
    
    * Fix CI
---
 .../api_fastapi/core_api/routes/public/event_logs.py       |  9 ++++++++-
 airflow-core/src/airflow/api_fastapi/core_api/security.py  | 14 ++++++++++++++
 .../api_fastapi/core_api/routes/public/test_event_logs.py  |  4 ++--
 3 files changed, 24 insertions(+), 3 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 8909b5ff1bc..529fbb94a6b 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
@@ -43,7 +43,11 @@ from airflow.api_fastapi.core_api.datamodels.event_logs 
import (
     EventLogResponse,
 )
 from airflow.api_fastapi.core_api.openapi.exceptions import 
create_openapi_http_exception_doc
-from airflow.api_fastapi.core_api.security import DagAccessEntity, 
requires_access_dag
+from airflow.api_fastapi.core_api.security import (
+    DagAccessEntity,
+    ReadableEventLogsFilterDep,
+    requires_access_dag,
+)
 from airflow.models import Log
 
 event_logs_router = AirflowRouter(tags=["Event Log"], prefix="/eventLogs")
@@ -126,6 +130,7 @@ def get_event_logs(
     run_id_pattern: Annotated[_SearchParam, 
Depends(search_param_factory(Log.run_id, "run_id_pattern"))],
     owner_pattern: Annotated[_SearchParam, 
Depends(search_param_factory(Log.owner, "owner_pattern"))],
     event_pattern: Annotated[_SearchParam, 
Depends(search_param_factory(Log.event, "event_pattern"))],
+    readable_event_logs_filter: ReadableEventLogsFilterDep,
 ) -> EventLogCollectionResponse:
     """Get all Event Logs."""
     query = select(Log).options(joinedload(Log.task_instance), 
joinedload(Log.dag_model))
@@ -151,6 +156,8 @@ def get_event_logs(
             run_id_pattern,
             owner_pattern,
             event_pattern,
+            # Permission
+            readable_event_logs_filter,
         ],
         offset=offset,
         limit=limit,
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 adca02f2e77..3b92e845e73 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/security.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/security.py
@@ -25,6 +25,7 @@ from fastapi import Depends, HTTPException, Request, status
 from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer, 
OAuth2PasswordBearer
 from jwt import ExpiredSignatureError, InvalidTokenError
 from pydantic import NonNegativeInt
+from sqlalchemy import or_
 
 from airflow.api_fastapi.app import get_auth_manager
 from airflow.api_fastapi.auth.managers.base_auth_manager import (
@@ -65,6 +66,7 @@ from airflow.configuration import conf
 from airflow.models import Connection, Pool, Variable
 from airflow.models.dag import DagModel, DagRun, DagTag
 from airflow.models.dagwarning import DagWarning
+from airflow.models.log import Log
 from airflow.models.taskinstance import TaskInstance as TI
 from airflow.models.team import Team
 from airflow.models.xcom import XComModel
@@ -188,6 +190,15 @@ class PermittedDagWarningFilter(PermittedDagFilter):
         return select.where(DagWarning.dag_id.in_(self.value or set()))
 
 
+class PermittedEventLogFilter(PermittedDagFilter):
+    """A parameter that filters the permitted even logs for the user."""
+
+    def to_orm(self, select: Select) -> Select:
+        # Event Logs not related to Dags have dag_id as None and are always 
returned.
+        # return select.where(Log.dag_id.in_(self.value or set()) or 
Log.dag_id.is_(None))
+        return select.where(or_(Log.dag_id.in_(self.value or set()), 
Log.dag_id.is_(None)))
+
+
 class PermittedTIFilter(PermittedDagFilter):
     """A parameter that filters the permitted task instances for the user."""
 
@@ -240,6 +251,9 @@ ReadableDagWarningsFilterDep = Annotated[
 ReadableTIFilterDep = Annotated[
     PermittedTIFilter, Depends(permitted_dag_filter_factory("GET", 
PermittedTIFilter))
 ]
+ReadableEventLogsFilterDep = Annotated[
+    PermittedTIFilter, Depends(permitted_dag_filter_factory("GET", 
PermittedEventLogFilter))
+]
 ReadableXComFilterDep = Annotated[
     PermittedXComFilter, Depends(permitted_dag_filter_factory("GET", 
PermittedXComFilter))
 ]
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_event_logs.py 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_event_logs.py
index c71dc587a15..3decc63918a 100644
--- 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_event_logs.py
+++ 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_event_logs.py
@@ -302,7 +302,7 @@ class TestGetEventLogs(TestEventLogsEndpoint):
     def test_get_event_logs(
         self, test_client, query_params, expected_status_code, 
expected_total_entries, expected_events
     ):
-        with assert_queries_count(2):
+        with assert_queries_count(3):
             response = test_client.get("/eventLogs", params=query_params)
         assert response.status_code == expected_status_code
         if expected_status_code != 200:
@@ -341,7 +341,7 @@ class TestGetEventLogs(TestEventLogsEndpoint):
     def test_get_event_logs_order_by(
         self, test_client, query_params, expected_status_code, 
expected_total_entries, expected_events
     ):
-        with assert_queries_count(2):
+        with assert_queries_count(3):
             response = test_client.get("/eventLogs", params=query_params)
         assert response.status_code == expected_status_code
         if expected_status_code != 200:

Reply via email to