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

pierrejeambrun pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-1-test by this push:
     new 813232cbc8b [v3-1-test] Fix list dag versions permissions (#61675) 
(#61733)
813232cbc8b is described below

commit 813232cbc8be8229de9bed62f4efc9fce2de11bc
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Wed Feb 11 20:00:38 2026 +0100

    [v3-1-test] Fix list dag versions permissions (#61675) (#61733)
    
    * Fix list dag versions permissions
    
    * Add a test
    
    * Address review comments
    (cherry picked from commit 197edfd873a1307090688aafd05695eb54724af3)
    
    Co-authored-by: Pierre Jeambrun <[email protected]>
---
 .../core_api/routes/public/dag_versions.py         |  8 +++++--
 .../src/airflow/api_fastapi/core_api/security.py   | 11 +++++++++
 .../core_api/routes/public/test_dag_versions.py    | 26 +++++++++++++++++-----
 3 files changed, 38 insertions(+), 7 deletions(-)

diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/dag_versions.py 
b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/dag_versions.py
index 5f1c734148b..bbb99441f81 100644
--- 
a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/dag_versions.py
+++ 
b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/dag_versions.py
@@ -38,7 +38,10 @@ from airflow.api_fastapi.core_api.datamodels.dag_versions 
import (
     DagVersionResponse,
 )
 from airflow.api_fastapi.core_api.openapi.exceptions import 
create_openapi_http_exception_doc
-from airflow.api_fastapi.core_api.security import requires_access_dag
+from airflow.api_fastapi.core_api.security import (
+    ReadableDagVersionsFilterDep,
+    requires_access_dag,
+)
 from airflow.models.dag_version import DagVersion
 
 dag_versions_router = AirflowRouter(tags=["DagVersion"], 
prefix="/dags/{dag_id}/dagVersions")
@@ -102,6 +105,7 @@ def get_dag_versions(
         ),
     ],
     dag_bag: DagBagDep,
+    readable_dag_versions_filter: ReadableDagVersionsFilterDep,
 ) -> DAGVersionCollectionResponse:
     """
     Get all DAG Versions.
@@ -116,7 +120,7 @@ def get_dag_versions(
 
     dag_versions_select, total_entries = paginated_select(
         statement=query,
-        filters=[version_number, bundle_name, bundle_version],
+        filters=[version_number, bundle_name, bundle_version, 
readable_dag_versions_filter],
         order_by=order_by,
         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 9d31ed4bd56..4e2d7f6c7ce 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/security.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/security.py
@@ -64,6 +64,7 @@ from airflow.configuration import conf
 from airflow.models import Connection, Pool, Variable
 from airflow.models.backfill import Backfill
 from airflow.models.dag import DagModel, DagRun, DagTag
+from airflow.models.dag_version import DagVersion
 from airflow.models.dagwarning import DagWarning
 from airflow.models.log import Log
 from airflow.models.taskinstance import TaskInstance as TI
@@ -211,6 +212,13 @@ class PermittedTagFilter(PermittedDagFilter):
         return select.where(DagTag.dag_id.in_(self.value))
 
 
+class PermittedDagVersionFilter(PermittedDagFilter):
+    """A parameter that filters the permitted dag versions for the user."""
+
+    def to_orm(self, select: Select) -> Select:
+        return select.where(DagVersion.dag_id.in_(self.value or set()))
+
+
 def permitted_dag_filter_factory(
     method: ResourceMethod, filter_class=PermittedDagFilter
 ) -> Callable[[Request, BaseUser], PermittedDagFilter]:
@@ -253,6 +261,9 @@ ReadableXComFilterDep = Annotated[
 ReadableTagsFilterDep = Annotated[
     PermittedTagFilter, Depends(permitted_dag_filter_factory("GET", 
PermittedTagFilter))
 ]
+ReadableDagVersionsFilterDep = Annotated[
+    PermittedDagVersionFilter, Depends(permitted_dag_filter_factory("GET", 
PermittedDagVersionFilter))
+]
 
 
 def requires_access_backfill(
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_versions.py
 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_versions.py
index 506875b2e35..6e7971f76d6 100644
--- 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_versions.py
+++ 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_versions.py
@@ -262,7 +262,7 @@ class TestGetDagVersions(TestDagVersionEndpoint):
                     ],
                     "total_entries": 4,
                 },
-                2,
+                3,
             ],
             [
                 "dag_with_multiple_versions",
@@ -301,7 +301,7 @@ class TestGetDagVersions(TestDagVersionEndpoint):
                     ],
                     "total_entries": 3,
                 },
-                4,
+                5,
             ],
         ],
     )
@@ -312,6 +312,22 @@ class TestGetDagVersions(TestDagVersionEndpoint):
         assert response.status_code == 200
         assert response.json() == expected_response
 
+    @pytest.mark.usefixtures("make_dag_with_multiple_versions")
+    @mock.patch(
+        
"airflow.api_fastapi.auth.managers.base_auth_manager.BaseAuthManager.get_authorized_dag_ids",
+        return_value={"dag_with_multiple_versions"},
+    )
+    def test_get_dag_versions_permission_filtering(self, _, test_client):
+        """Test that listing all DAG versions with ~ only returns versions for 
permitted DAGs."""
+        with assert_queries_count(4):
+            response = test_client.get("/dags/~/dagVersions")
+
+        assert response.status_code == 200
+        body = response.json()
+        assert body["total_entries"] == 3
+        dag_ids = {v["dag_id"] for v in body["dag_versions"]}
+        assert dag_ids == {"dag_with_multiple_versions"}
+
     @pytest.mark.parametrize(
         "dag_id, expected_response, expected_query_count",
         [
@@ -362,7 +378,7 @@ class TestGetDagVersions(TestDagVersionEndpoint):
                     ],
                     "total_entries": 4,
                 },
-                2,
+                3,
             ],
             [
                 "dag_with_multiple_versions",
@@ -401,7 +417,7 @@ class TestGetDagVersions(TestDagVersionEndpoint):
                     ],
                     "total_entries": 3,
                 },
-                4,
+                5,
             ],
         ],
     )
@@ -488,7 +504,7 @@ class TestGetDagVersions(TestDagVersionEndpoint):
     def test_get_dag_versions_parameters(
         self, test_client, params, expected_versions, expected_total_entries
     ):
-        with assert_queries_count(2):
+        with assert_queries_count(3):
             response = test_client.get("/dags/~/dagVersions", params=params)
         assert response.status_code == 200
         response_payload = response.json()

Reply via email to