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

vincbeck 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 0f68191fbbc FAB: fix 403 from roles endpoint despite admin rights 
(#64097)
0f68191fbbc is described below

commit 0f68191fbbc72dc18de42456a492b5b9115d5207
Author: Dev-iL <[email protected]>
AuthorDate: Mon Mar 23 15:01:03 2026 +0200

    FAB: fix 403 from roles endpoint despite admin rights (#64097)
    
    * fix(providers/fab): map PATCH to can_edit in FAB action map
    
    Widen `_MAP_METHOD_NAME_TO_FAB_ACTION_NAME` from 
`dict[ExtendedResourceMethod, str]`
    to `dict[str, str]` and add a `"PATCH" -> ACTION_CAN_EDIT` entry so that
    PATCH endpoints resolve to the correct FAB permission without extending
    the auth-model enums.
    
    Closes: #59510
    
    * fix(providers/fab): use forward method→action map in 
get_authorized_dag_ids
    
    The PATCH→can_edit entry added in 839e2e9 broke get_authorized_dag_ids
    for PUT requests. The method used the *reverse* map (action→method) to
    match permissions, but reversing a many-to-one map (PUT and PATCH both
    map to can_edit) silently dropped PUT in favour of PATCH.
    
    Switch to the forward map instead: look up the FAB action for the
    incoming HTTP method once, then compare directly against each
    permission's action name. This is both correct for aliased methods and
    simpler.
    
    ---------
    
    Co-authored-by: Yoann Abriel <[email protected]>
---
 .../airflow/providers/fab/auth_manager/fab_auth_manager.py | 13 +++----------
 providers/fab/src/airflow/providers/fab/www/utils.py       | 14 ++++++--------
 providers/fab/tests/unit/fab/www/test_utils.py             | 11 ++++++++++-
 3 files changed, 19 insertions(+), 19 deletions(-)

diff --git 
a/providers/fab/src/airflow/providers/fab/auth_manager/fab_auth_manager.py 
b/providers/fab/src/airflow/providers/fab/auth_manager/fab_auth_manager.py
index 63c83040c5c..c2800eb0443 100644
--- a/providers/fab/src/airflow/providers/fab/auth_manager/fab_auth_manager.py
+++ b/providers/fab/src/airflow/providers/fab/auth_manager/fab_auth_manager.py
@@ -88,10 +88,7 @@ from airflow.providers.fab.www.security.permissions import (
     RESOURCE_WEBSITE,
     RESOURCE_XCOM,
 )
-from airflow.providers.fab.www.utils import (
-    get_fab_action_from_method_map,
-    get_method_from_fab_action_map,
-)
+from airflow.providers.fab.www.utils import get_fab_action_from_method_map
 from airflow.utils.session import NEW_SESSION, provide_session
 
 if TYPE_CHECKING:
@@ -524,15 +521,11 @@ class FabAuthManager(BaseAuthManager[User]):
         )
         roles = user_query.roles
 
-        map_fab_action_name_to_method_name = get_method_from_fab_action_map()
+        fab_action = get_fab_action_from_method_map().get(method)
         resources = set()
         for role in roles:
             for permission in role.permissions:
-                action = permission.action.name
-                if (
-                    action in map_fab_action_name_to_method_name
-                    and map_fab_action_name_to_method_name[action] == method
-                ):
+                if permission.action.name == fab_action:
                     resource = permission.resource.name
                     if resource == permissions.RESOURCE_DAG:
                         return {dag.dag_id for dag in 
session.execute(select(DagModel.dag_id))}
diff --git a/providers/fab/src/airflow/providers/fab/www/utils.py 
b/providers/fab/src/airflow/providers/fab/www/utils.py
index cff20a01cb6..7499debc965 100644
--- a/providers/fab/src/airflow/providers/fab/www/utils.py
+++ b/providers/fab/src/airflow/providers/fab/www/utils.py
@@ -31,19 +31,17 @@ from airflow.providers.fab.www.security.permissions import (
 )
 
 if TYPE_CHECKING:
-    try:
-        from airflow.api_fastapi.auth.managers.base_auth_manager import 
ExtendedResourceMethod
-    except ImportError:
-        from airflow.api_fastapi.auth.managers.base_auth_manager import (
-            ResourceMethod as ExtendedResourceMethod,
-        )
     from airflow.providers.fab.auth_manager.fab_auth_manager import 
FabAuthManager
 
-# Convert methods to FAB action name
-_MAP_METHOD_NAME_TO_FAB_ACTION_NAME: dict[ExtendedResourceMethod, str] = {
+# Convert methods to FAB action name.
+# Keyed on ``str`` rather than ``ExtendedResourceMethod`` so that HTTP verbs
+# not part of the auth model (e.g. PATCH) can be mapped to FAB actions
+# without extending the enum.  See 
https://github.com/apache/airflow/issues/59510
+_MAP_METHOD_NAME_TO_FAB_ACTION_NAME: dict[str, str] = {
     "POST": ACTION_CAN_CREATE,
     "GET": ACTION_CAN_READ,
     "PUT": ACTION_CAN_EDIT,
+    "PATCH": ACTION_CAN_EDIT,
     "DELETE": ACTION_CAN_DELETE,
     "MENU": ACTION_CAN_ACCESS_MENU,
 }
diff --git a/providers/fab/tests/unit/fab/www/test_utils.py 
b/providers/fab/tests/unit/fab/www/test_utils.py
index c87b4a866f3..e75fc62b73b 100644
--- a/providers/fab/tests/unit/fab/www/test_utils.py
+++ b/providers/fab/tests/unit/fab/www/test_utils.py
@@ -17,11 +17,20 @@
 
 from __future__ import annotations
 
-from airflow.providers.fab.www.utils import get_session_lifetime_config
+from airflow.providers.fab.www.security.permissions import ACTION_CAN_EDIT
+from airflow.providers.fab.www.utils import get_fab_action_from_method_map, 
get_session_lifetime_config
 
 from tests_common.test_utils.config import conf_vars
 
 
+class TestMethodToFabActionMap:
+    def test_patch_maps_to_can_edit(self):
+        """PATCH should resolve to the same FAB action as PUT (can_edit)."""
+        method_map = get_fab_action_from_method_map()
+        assert method_map["PATCH"] == ACTION_CAN_EDIT
+        assert method_map["PUT"] == ACTION_CAN_EDIT
+
+
 class TestUpdatedConfigNames:
     @conf_vars({("fab", "session_lifetime_minutes"): "43200"})
     def test_config_val_is_default(self):

Reply via email to