This is an automated email from the ASF dual-hosted git repository.
jasonliu 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 b696a5cee9e Deprecate has access backfill in providers (#61402)
b696a5cee9e is described below
commit b696a5cee9ea983fd20ee1f380d4e0be2c58f356
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Thu Feb 5 05:21:41 2026 +0100
Deprecate has access backfill in providers (#61402)
* Deprecate 'is_authorized_backfill' in providers
* Address review comment
* Fix CI
* Fix CI
---
.../amazon/aws/auth_manager/aws_auth_manager.py | 9 +++++
.../aws/auth_manager/test_aws_auth_manager.py | 15 +++++++-
.../providers/fab/auth_manager/fab_auth_manager.py | 9 ++++-
.../unit/fab/auth_manager/test_fab_auth_manager.py | 21 ++++++++---
.../keycloak/auth_manager/keycloak_auth_manager.py | 9 +++++
.../auth_manager/test_keycloak_auth_manager.py | 43 ++++++++++++++++++----
6 files changed, 90 insertions(+), 16 deletions(-)
diff --git
a/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py
b/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py
index aa23b4f2634..9d3f2fabe9e 100644
---
a/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py
+++
b/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py
@@ -16,6 +16,7 @@
# under the License.
from __future__ import annotations
+import warnings
from collections import defaultdict
from collections.abc import Sequence
from functools import cached_property
@@ -27,6 +28,7 @@ from fastapi import FastAPI
from airflow.api_fastapi.app import AUTH_MANAGER_FASTAPI_APP_PREFIX
from airflow.api_fastapi.auth.managers.base_auth_manager import BaseAuthManager
from airflow.cli.cli_config import CLICommand
+from airflow.exceptions import AirflowProviderDeprecationWarning
from airflow.providers.amazon.aws.auth_manager.avp.entities import AvpEntities
from airflow.providers.amazon.aws.auth_manager.avp.facade import (
AwsAuthManagerAmazonVerifiedPermissionsFacade,
@@ -158,6 +160,13 @@ class AwsAuthManager(BaseAuthManager[AwsAuthManagerUser]):
def is_authorized_backfill(
self, *, method: ResourceMethod, user: AwsAuthManagerUser, details:
BackfillDetails | None = None
) -> bool:
+ # Method can be removed once the min Airflow version is >= 3.2.0.
+ warnings.warn(
+ "Use ``is_authorized_dag`` on ``DagAccessEntity.RUN`` instead for
a dag level access control.",
+ AirflowProviderDeprecationWarning,
+ stacklevel=2,
+ )
+
backfill_id = details.id if details else None
return self.avp_facade.is_authorized(
method=method, entity_type=AvpEntities.BACKFILL, user=user,
entity_id=backfill_id
diff --git
a/providers/amazon/tests/unit/amazon/aws/auth_manager/test_aws_auth_manager.py
b/providers/amazon/tests/unit/amazon/aws/auth_manager/test_aws_auth_manager.py
index 8ee6fa8470b..8e0ff06c185 100644
---
a/providers/amazon/tests/unit/amazon/aws/auth_manager/test_aws_auth_manager.py
+++
b/providers/amazon/tests/unit/amazon/aws/auth_manager/test_aws_auth_manager.py
@@ -16,11 +16,14 @@
# under the License.
from __future__ import annotations
+from contextlib import ExitStack
from typing import TYPE_CHECKING
from unittest.mock import ANY, Mock, patch
import pytest
+from airflow.exceptions import AirflowProviderDeprecationWarning
+
from tests_common.test_utils.version_compat import AIRFLOW_V_3_0_PLUS
if not AIRFLOW_V_3_0_PLUS:
@@ -226,8 +229,16 @@ class TestAwsAuthManager:
is_authorized = Mock(return_value=True)
mock_avp_facade.is_authorized = is_authorized
- method: ResourceMethod = "GET"
- result = auth_manager.is_authorized_backfill(method=method,
details=details, user=user)
+ with ExitStack() as stack:
+ stack.enter_context(
+ pytest.warns(
+ AirflowProviderDeprecationWarning,
+ match="Use ``is_authorized_dag`` on
``DagAccessEntity.RUN`` instead for a dag level access control.",
+ )
+ )
+
+ method: ResourceMethod = "GET"
+ result = auth_manager.is_authorized_backfill(method=method,
details=details, user=user)
is_authorized.assert_called_once_with(
method=method, entity_type=AvpEntities.BACKFILL,
user=expected_user, entity_id=expected_entity_id
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 aebd4fa6f14..14f7eb1f41f 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
@@ -17,6 +17,7 @@
# under the License.
from __future__ import annotations
+import warnings
from functools import cached_property
from pathlib import Path
from typing import TYPE_CHECKING, Any
@@ -51,7 +52,7 @@ from
airflow.api_fastapi.auth.managers.models.resource_details import (
)
from airflow.api_fastapi.common.types import ExtraMenuItem, MenuItem
from airflow.configuration import conf
-from airflow.exceptions import AirflowConfigException
+from airflow.exceptions import AirflowConfigException,
AirflowProviderDeprecationWarning
from airflow.models import Connection, DagModel, Pool, Variable
from airflow.providers.common.compat.sdk import AirflowException
from airflow.providers.fab.auth_manager.models import Permission, Role, User
@@ -389,6 +390,12 @@ class FabAuthManager(BaseAuthManager[User]):
user: User,
details: BackfillDetails | None = None,
) -> bool:
+ # Method can be removed once the min Airflow version is >= 3.2.0.
+ warnings.warn(
+ "Use ``is_authorized_dag`` on ``DagAccessEntity.RUN`` instead for
a dag level access control.",
+ AirflowProviderDeprecationWarning,
+ stacklevel=2,
+ )
return self._is_authorized(method=method,
resource_type=RESOURCE_BACKFILL, user=user)
def is_authorized_asset(
diff --git a/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py
b/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py
index 0170d14923f..d9c7bbc9f91 100644
--- a/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py
+++ b/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py
@@ -17,7 +17,7 @@
from __future__ import annotations
import time
-from contextlib import contextmanager, suppress
+from contextlib import ExitStack, contextmanager, suppress
from itertools import chain
from typing import TYPE_CHECKING
from unittest import mock
@@ -29,7 +29,7 @@ from flask_appbuilder.const import AUTH_DB, AUTH_LDAP
from airflow.api_fastapi.app import AUTH_MANAGER_FASTAPI_APP_PREFIX
from airflow.api_fastapi.common.types import MenuItem
-from airflow.exceptions import AirflowConfigException
+from airflow.exceptions import AirflowConfigException,
AirflowProviderDeprecationWarning
from airflow.providers.fab.www.app import create_app
from airflow.providers.fab.www.utils import get_fab_auth_manager
from airflow.providers.standard.operators.empty import EmptyOperator
@@ -328,10 +328,19 @@ class TestFabAuthManager:
def test_is_authorized(self, api_name, method, user_permissions,
expected_result, auth_manager):
user = Mock()
user.perms = user_permissions
- result = getattr(auth_manager, api_name)(
- method=method,
- user=user,
- )
+
+ with ExitStack() as stack:
+ if api_name == "is_authorized_backfill":
+ stack.enter_context(
+ pytest.warns(
+ AirflowProviderDeprecationWarning,
+ match="Use ``is_authorized_dag`` on
``DagAccessEntity.RUN`` instead for a dag level access control.",
+ )
+ )
+ result = getattr(auth_manager, api_name)(
+ method=method,
+ user=user,
+ )
assert result == expected_result
@pytest.mark.parametrize(
diff --git
a/providers/keycloak/src/airflow/providers/keycloak/auth_manager/keycloak_auth_manager.py
b/providers/keycloak/src/airflow/providers/keycloak/auth_manager/keycloak_auth_manager.py
index dc8de7c2595..746a5a428cb 100644
---
a/providers/keycloak/src/airflow/providers/keycloak/auth_manager/keycloak_auth_manager.py
+++
b/providers/keycloak/src/airflow/providers/keycloak/auth_manager/keycloak_auth_manager.py
@@ -19,6 +19,7 @@ from __future__ import annotations
import json
import logging
import time
+import warnings
from base64 import urlsafe_b64decode
from typing import TYPE_CHECKING, Any
from urllib.parse import urljoin
@@ -32,6 +33,7 @@ from urllib3.util import Retry
from airflow.api_fastapi.app import AUTH_MANAGER_FASTAPI_APP_PREFIX
from airflow.api_fastapi.auth.managers.base_auth_manager import BaseAuthManager
+from airflow.exceptions import AirflowProviderDeprecationWarning
try:
from airflow.api_fastapi.auth.managers.base_auth_manager import
ExtendedResourceMethod
@@ -220,6 +222,13 @@ class
KeycloakAuthManager(BaseAuthManager[KeycloakAuthManagerUser]):
def is_authorized_backfill(
self, *, method: ResourceMethod, user: KeycloakAuthManagerUser,
details: BackfillDetails | None = None
) -> bool:
+ # Method can be removed once the min Airflow version is >= 3.2.0.
+ warnings.warn(
+ "Use ``is_authorized_dag`` on ``DagAccessEntity.RUN`` instead for
a dag level access control.",
+ AirflowProviderDeprecationWarning,
+ stacklevel=2,
+ )
+
backfill_id = str(details.id) if details else None
return self._is_authorized(
method=method, resource_type=KeycloakResource.BACKFILL, user=user,
resource_id=backfill_id
diff --git
a/providers/keycloak/tests/unit/keycloak/auth_manager/test_keycloak_auth_manager.py
b/providers/keycloak/tests/unit/keycloak/auth_manager/test_keycloak_auth_manager.py
index 27747f50696..faf954eb2ed 100644
---
a/providers/keycloak/tests/unit/keycloak/auth_manager/test_keycloak_auth_manager.py
+++
b/providers/keycloak/tests/unit/keycloak/auth_manager/test_keycloak_auth_manager.py
@@ -17,6 +17,7 @@
from __future__ import annotations
import json
+from contextlib import ExitStack
from unittest.mock import Mock, patch
import pytest
@@ -36,6 +37,7 @@ from
airflow.api_fastapi.auth.managers.models.resource_details import (
VariableDetails,
)
from airflow.api_fastapi.common.types import MenuItem
+from airflow.exceptions import AirflowProviderDeprecationWarning
from airflow.providers.common.compat.sdk import AirflowException
from airflow.providers.keycloak.auth_manager.constants import (
CONF_CLIENT_ID_KEY,
@@ -263,7 +265,16 @@ class TestKeycloakAuthManager:
mock_response.status_code = status_code
auth_manager.http_session.post = Mock(return_value=mock_response)
- result = getattr(auth_manager, function)(method=method, user=user,
details=details)
+ with ExitStack() as stack:
+ if function == "is_authorized_backfill":
+ stack.enter_context(
+ pytest.warns(
+ AirflowProviderDeprecationWarning,
+ match="Use ``is_authorized_dag`` on
``DagAccessEntity.RUN`` instead for a dag level access control.",
+ )
+ )
+
+ result = getattr(auth_manager, function)(method=method, user=user,
details=details)
token_url = auth_manager._get_token_url("server_url", "realm")
payload = auth_manager._get_payload("client_id", permission,
attributes)
@@ -291,10 +302,19 @@ class TestKeycloakAuthManager:
resp.status_code = 500
auth_manager.http_session.post = Mock(return_value=resp)
- with pytest.raises(AirflowException) as e:
- getattr(auth_manager, function)(method="GET", user=user)
+ with ExitStack() as stack:
+ if function == "is_authorized_backfill":
+ stack.enter_context(
+ pytest.warns(
+ AirflowProviderDeprecationWarning,
+ match="Use ``is_authorized_dag`` on
``DagAccessEntity.RUN`` instead for a dag level access control.",
+ )
+ )
+
+ with pytest.raises(AirflowException) as e:
+ getattr(auth_manager, function)(method="GET", user=user)
- assert "Unexpected error" in str(e.value)
+ assert "Unexpected error" in str(e.value)
@pytest.mark.parametrize(
"function",
@@ -315,10 +335,19 @@ class TestKeycloakAuthManager:
resp.text = json.dumps({"error": "invalid_scope", "error_description":
"Invalid scopes: GET"})
auth_manager.http_session.post = Mock(return_value=resp)
- with pytest.raises(AirflowException) as e:
- getattr(auth_manager, function)(method="GET", user=user)
+ with ExitStack() as stack:
+ if function == "is_authorized_backfill":
+ stack.enter_context(
+ pytest.warns(
+ AirflowProviderDeprecationWarning,
+ match="Use ``is_authorized_dag`` on
``DagAccessEntity.RUN`` instead for a dag level access control.",
+ )
+ )
+
+ with pytest.raises(AirflowException) as e:
+ getattr(auth_manager, function)(method="GET", user=user)
- assert "Request not recognized by Keycloak. invalid_scope. Invalid
scopes: GET" in str(e.value)
+ assert "Request not recognized by Keycloak. invalid_scope. Invalid
scopes: GET" in str(e.value)
@pytest.mark.parametrize(
("method", "access_entity", "details", "permission", "attributes"),