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):