Skip to content

Commit b696a5c

Browse files
Deprecate has access backfill in providers (#61402)
* Deprecate 'is_authorized_backfill' in providers * Address review comment * Fix CI * Fix CI
1 parent 2f213b2 commit b696a5c

6 files changed

Lines changed: 90 additions & 16 deletions

File tree

providers/amazon/src/airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# under the License.
1717
from __future__ import annotations
1818

19+
import warnings
1920
from collections import defaultdict
2021
from collections.abc import Sequence
2122
from functools import cached_property
@@ -27,6 +28,7 @@
2728
from airflow.api_fastapi.app import AUTH_MANAGER_FASTAPI_APP_PREFIX
2829
from airflow.api_fastapi.auth.managers.base_auth_manager import BaseAuthManager
2930
from airflow.cli.cli_config import CLICommand
31+
from airflow.exceptions import AirflowProviderDeprecationWarning
3032
from airflow.providers.amazon.aws.auth_manager.avp.entities import AvpEntities
3133
from airflow.providers.amazon.aws.auth_manager.avp.facade import (
3234
AwsAuthManagerAmazonVerifiedPermissionsFacade,
@@ -158,6 +160,13 @@ def is_authorized_dag(
158160
def is_authorized_backfill(
159161
self, *, method: ResourceMethod, user: AwsAuthManagerUser, details: BackfillDetails | None = None
160162
) -> bool:
163+
# Method can be removed once the min Airflow version is >= 3.2.0.
164+
warnings.warn(
165+
"Use ``is_authorized_dag`` on ``DagAccessEntity.RUN`` instead for a dag level access control.",
166+
AirflowProviderDeprecationWarning,
167+
stacklevel=2,
168+
)
169+
161170
backfill_id = details.id if details else None
162171
return self.avp_facade.is_authorized(
163172
method=method, entity_type=AvpEntities.BACKFILL, user=user, entity_id=backfill_id

providers/amazon/tests/unit/amazon/aws/auth_manager/test_aws_auth_manager.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616
# under the License.
1717
from __future__ import annotations
1818

19+
from contextlib import ExitStack
1920
from typing import TYPE_CHECKING
2021
from unittest.mock import ANY, Mock, patch
2122

2223
import pytest
2324

25+
from airflow.exceptions import AirflowProviderDeprecationWarning
26+
2427
from tests_common.test_utils.version_compat import AIRFLOW_V_3_0_PLUS
2528

2629
if not AIRFLOW_V_3_0_PLUS:
@@ -226,8 +229,16 @@ def test_is_authorized_backfill(
226229
is_authorized = Mock(return_value=True)
227230
mock_avp_facade.is_authorized = is_authorized
228231

229-
method: ResourceMethod = "GET"
230-
result = auth_manager.is_authorized_backfill(method=method, details=details, user=user)
232+
with ExitStack() as stack:
233+
stack.enter_context(
234+
pytest.warns(
235+
AirflowProviderDeprecationWarning,
236+
match="Use ``is_authorized_dag`` on ``DagAccessEntity.RUN`` instead for a dag level access control.",
237+
)
238+
)
239+
240+
method: ResourceMethod = "GET"
241+
result = auth_manager.is_authorized_backfill(method=method, details=details, user=user)
231242

232243
is_authorized.assert_called_once_with(
233244
method=method, entity_type=AvpEntities.BACKFILL, user=expected_user, entity_id=expected_entity_id

providers/fab/src/airflow/providers/fab/auth_manager/fab_auth_manager.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
# under the License.
1818
from __future__ import annotations
1919

20+
import warnings
2021
from functools import cached_property
2122
from pathlib import Path
2223
from typing import TYPE_CHECKING, Any
@@ -51,7 +52,7 @@
5152
)
5253
from airflow.api_fastapi.common.types import ExtraMenuItem, MenuItem
5354
from airflow.configuration import conf
54-
from airflow.exceptions import AirflowConfigException
55+
from airflow.exceptions import AirflowConfigException, AirflowProviderDeprecationWarning
5556
from airflow.models import Connection, DagModel, Pool, Variable
5657
from airflow.providers.common.compat.sdk import AirflowException
5758
from airflow.providers.fab.auth_manager.models import Permission, Role, User
@@ -389,6 +390,12 @@ def is_authorized_backfill(
389390
user: User,
390391
details: BackfillDetails | None = None,
391392
) -> bool:
393+
# Method can be removed once the min Airflow version is >= 3.2.0.
394+
warnings.warn(
395+
"Use ``is_authorized_dag`` on ``DagAccessEntity.RUN`` instead for a dag level access control.",
396+
AirflowProviderDeprecationWarning,
397+
stacklevel=2,
398+
)
392399
return self._is_authorized(method=method, resource_type=RESOURCE_BACKFILL, user=user)
393400

394401
def is_authorized_asset(

providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from __future__ import annotations
1818

1919
import time
20-
from contextlib import contextmanager, suppress
20+
from contextlib import ExitStack, contextmanager, suppress
2121
from itertools import chain
2222
from typing import TYPE_CHECKING
2323
from unittest import mock
@@ -29,7 +29,7 @@
2929

3030
from airflow.api_fastapi.app import AUTH_MANAGER_FASTAPI_APP_PREFIX
3131
from airflow.api_fastapi.common.types import MenuItem
32-
from airflow.exceptions import AirflowConfigException
32+
from airflow.exceptions import AirflowConfigException, AirflowProviderDeprecationWarning
3333
from airflow.providers.fab.www.app import create_app
3434
from airflow.providers.fab.www.utils import get_fab_auth_manager
3535
from airflow.providers.standard.operators.empty import EmptyOperator
@@ -328,10 +328,19 @@ def test_create_token_wrong_values(self, username, password, auth_manager_with_a
328328
def test_is_authorized(self, api_name, method, user_permissions, expected_result, auth_manager):
329329
user = Mock()
330330
user.perms = user_permissions
331-
result = getattr(auth_manager, api_name)(
332-
method=method,
333-
user=user,
334-
)
331+
332+
with ExitStack() as stack:
333+
if api_name == "is_authorized_backfill":
334+
stack.enter_context(
335+
pytest.warns(
336+
AirflowProviderDeprecationWarning,
337+
match="Use ``is_authorized_dag`` on ``DagAccessEntity.RUN`` instead for a dag level access control.",
338+
)
339+
)
340+
result = getattr(auth_manager, api_name)(
341+
method=method,
342+
user=user,
343+
)
335344
assert result == expected_result
336345

337346
@pytest.mark.parametrize(

providers/keycloak/src/airflow/providers/keycloak/auth_manager/keycloak_auth_manager.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import json
2020
import logging
2121
import time
22+
import warnings
2223
from base64 import urlsafe_b64decode
2324
from typing import TYPE_CHECKING, Any
2425
from urllib.parse import urljoin
@@ -32,6 +33,7 @@
3233

3334
from airflow.api_fastapi.app import AUTH_MANAGER_FASTAPI_APP_PREFIX
3435
from airflow.api_fastapi.auth.managers.base_auth_manager import BaseAuthManager
36+
from airflow.exceptions import AirflowProviderDeprecationWarning
3537

3638
try:
3739
from airflow.api_fastapi.auth.managers.base_auth_manager import ExtendedResourceMethod
@@ -220,6 +222,13 @@ def is_authorized_dag(
220222
def is_authorized_backfill(
221223
self, *, method: ResourceMethod, user: KeycloakAuthManagerUser, details: BackfillDetails | None = None
222224
) -> bool:
225+
# Method can be removed once the min Airflow version is >= 3.2.0.
226+
warnings.warn(
227+
"Use ``is_authorized_dag`` on ``DagAccessEntity.RUN`` instead for a dag level access control.",
228+
AirflowProviderDeprecationWarning,
229+
stacklevel=2,
230+
)
231+
223232
backfill_id = str(details.id) if details else None
224233
return self._is_authorized(
225234
method=method, resource_type=KeycloakResource.BACKFILL, user=user, resource_id=backfill_id

providers/keycloak/tests/unit/keycloak/auth_manager/test_keycloak_auth_manager.py

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from __future__ import annotations
1818

1919
import json
20+
from contextlib import ExitStack
2021
from unittest.mock import Mock, patch
2122

2223
import pytest
@@ -36,6 +37,7 @@
3637
VariableDetails,
3738
)
3839
from airflow.api_fastapi.common.types import MenuItem
40+
from airflow.exceptions import AirflowProviderDeprecationWarning
3941
from airflow.providers.common.compat.sdk import AirflowException
4042
from airflow.providers.keycloak.auth_manager.constants import (
4143
CONF_CLIENT_ID_KEY,
@@ -263,7 +265,16 @@ def test_is_authorized(
263265
mock_response.status_code = status_code
264266
auth_manager.http_session.post = Mock(return_value=mock_response)
265267

266-
result = getattr(auth_manager, function)(method=method, user=user, details=details)
268+
with ExitStack() as stack:
269+
if function == "is_authorized_backfill":
270+
stack.enter_context(
271+
pytest.warns(
272+
AirflowProviderDeprecationWarning,
273+
match="Use ``is_authorized_dag`` on ``DagAccessEntity.RUN`` instead for a dag level access control.",
274+
)
275+
)
276+
277+
result = getattr(auth_manager, function)(method=method, user=user, details=details)
267278

268279
token_url = auth_manager._get_token_url("server_url", "realm")
269280
payload = auth_manager._get_payload("client_id", permission, attributes)
@@ -291,10 +302,19 @@ def test_is_authorized_failure(self, function, auth_manager, user):
291302
resp.status_code = 500
292303
auth_manager.http_session.post = Mock(return_value=resp)
293304

294-
with pytest.raises(AirflowException) as e:
295-
getattr(auth_manager, function)(method="GET", user=user)
305+
with ExitStack() as stack:
306+
if function == "is_authorized_backfill":
307+
stack.enter_context(
308+
pytest.warns(
309+
AirflowProviderDeprecationWarning,
310+
match="Use ``is_authorized_dag`` on ``DagAccessEntity.RUN`` instead for a dag level access control.",
311+
)
312+
)
313+
314+
with pytest.raises(AirflowException) as e:
315+
getattr(auth_manager, function)(method="GET", user=user)
296316

297-
assert "Unexpected error" in str(e.value)
317+
assert "Unexpected error" in str(e.value)
298318

299319
@pytest.mark.parametrize(
300320
"function",
@@ -315,10 +335,19 @@ def test_is_authorized_invalid_request(self, function, auth_manager, user):
315335
resp.text = json.dumps({"error": "invalid_scope", "error_description": "Invalid scopes: GET"})
316336
auth_manager.http_session.post = Mock(return_value=resp)
317337

318-
with pytest.raises(AirflowException) as e:
319-
getattr(auth_manager, function)(method="GET", user=user)
338+
with ExitStack() as stack:
339+
if function == "is_authorized_backfill":
340+
stack.enter_context(
341+
pytest.warns(
342+
AirflowProviderDeprecationWarning,
343+
match="Use ``is_authorized_dag`` on ``DagAccessEntity.RUN`` instead for a dag level access control.",
344+
)
345+
)
346+
347+
with pytest.raises(AirflowException) as e:
348+
getattr(auth_manager, function)(method="GET", user=user)
320349

321-
assert "Request not recognized by Keycloak. invalid_scope. Invalid scopes: GET" in str(e.value)
350+
assert "Request not recognized by Keycloak. invalid_scope. Invalid scopes: GET" in str(e.value)
322351

323352
@pytest.mark.parametrize(
324353
("method", "access_entity", "details", "permission", "attributes"),

0 commit comments

Comments
 (0)