diff --git a/airflow-core/src/airflow/models/dag.py b/airflow-core/src/airflow/models/dag.py index aa9ad78fbcce7..e9f8b2425ca66 100644 --- a/airflow-core/src/airflow/models/dag.py +++ b/airflow-core/src/airflow/models/dag.py @@ -96,7 +96,6 @@ from airflow.sdk.definitions.asset import Asset, AssetAlias, AssetUniqueKey, BaseAsset from airflow.sdk.definitions.dag import DAG as TaskSDKDag, dag as task_sdk_dag_decorator from airflow.secrets.local_filesystem import LocalFilesystemBackend -from airflow.security import permissions from airflow.settings import json from airflow.stats import Stats from airflow.timetables.base import DagRunInfo, DataInterval, TimeRestriction, Timetable @@ -468,6 +467,7 @@ def _upgrade_outdated_dag_access_control(access_control=None): return None from airflow.providers.fab import __version__ as FAB_VERSION + from airflow.providers.fab.www.security import permissions updated_access_control = {} for role, perms in access_control.items(): diff --git a/airflow-core/src/airflow/security/permissions.py b/airflow-core/src/airflow/security/permissions.py deleted file mode 100644 index 7545fe9d9305e..0000000000000 --- a/airflow-core/src/airflow/security/permissions.py +++ /dev/null @@ -1,116 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -from __future__ import annotations - -from typing import TypedDict - -# Resource Constants -RESOURCE_ACTION = "Permissions" -RESOURCE_ADMIN_MENU = "Admin" -RESOURCE_AUDIT_LOG = "Audit Logs" -RESOURCE_BACKFILL = "Backfills" -RESOURCE_BROWSE_MENU = "Browse" -RESOURCE_CONFIG = "Configurations" -RESOURCE_CONNECTION = "Connections" -RESOURCE_DAG = "DAGs" -RESOURCE_DAG_CODE = "DAG Code" -RESOURCE_DAG_DEPENDENCIES = "DAG Dependencies" -RESOURCE_DAG_PREFIX = "DAG:" -RESOURCE_DAG_RUN = "DAG Runs" -RESOURCE_DAG_RUN_PREFIX = "DAG Run:" -RESOURCE_DAG_VERSION = "DAG Versions" -RESOURCE_DAG_WARNING = "DAG Warnings" -RESOURCE_CLUSTER_ACTIVITY = "Cluster Activity" -RESOURCE_ASSET = "Assets" -RESOURCE_ASSET_ALIAS = "Asset Aliases" -RESOURCE_DOCS = "Documentation" -RESOURCE_DOCS_MENU = "Docs" -RESOURCE_IMPORT_ERROR = "ImportError" -RESOURCE_JOB = "Jobs" -RESOURCE_MY_PASSWORD = "My Password" -RESOURCE_MY_PROFILE = "My Profile" -RESOURCE_PASSWORD = "Passwords" -RESOURCE_PERMISSION = "Permission Views" # Refers to a Perm <-> View mapping, not an MVC View. -RESOURCE_PLUGIN = "Plugins" -RESOURCE_POOL = "Pools" -RESOURCE_PROVIDER = "Providers" -RESOURCE_RESOURCE = "View Menus" -RESOURCE_ROLE = "Roles" -RESOURCE_SLA_MISS = "SLA Misses" -RESOURCE_TASK_INSTANCE = "Task Instances" -RESOURCE_TASK_LOG = "Task Logs" -RESOURCE_TASK_RESCHEDULE = "Task Reschedules" -RESOURCE_TRIGGER = "Triggers" -RESOURCE_USER = "Users" -RESOURCE_USER_STATS_CHART = "User Stats Chart" -RESOURCE_VARIABLE = "Variables" -RESOURCE_WEBSITE = "Website" -RESOURCE_XCOM = "XComs" - -# Action Constants -ACTION_CAN_CREATE = "can_create" -ACTION_CAN_READ = "can_read" -ACTION_CAN_EDIT = "can_edit" -ACTION_CAN_DELETE = "can_delete" -ACTION_CAN_ACCESS_MENU = "menu_access" -DEPRECATED_ACTION_CAN_DAG_READ = "can_dag_read" -DEPRECATED_ACTION_CAN_DAG_EDIT = "can_dag_edit" - - -class ResourceDetails(TypedDict): - """Details of a resource (actions and prefix).""" - - actions: set[str] - prefix: str - - -# Keeping DAG_ACTIONS to keep the compatibility with outdated versions of FAB provider -DAG_ACTIONS = {ACTION_CAN_READ, ACTION_CAN_EDIT, ACTION_CAN_DELETE} - -RESOURCE_DETAILS_MAP = { - RESOURCE_DAG: ResourceDetails( - actions={ACTION_CAN_READ, ACTION_CAN_EDIT, ACTION_CAN_DELETE}, prefix=RESOURCE_DAG_PREFIX - ), - RESOURCE_DAG_RUN: ResourceDetails( - actions={ACTION_CAN_READ, ACTION_CAN_CREATE, ACTION_CAN_DELETE, ACTION_CAN_ACCESS_MENU}, - prefix=RESOURCE_DAG_RUN_PREFIX, - ), -} -PREFIX_LIST = [details["prefix"] for details in RESOURCE_DETAILS_MAP.values()] -PREFIX_RESOURCES_MAP = {details["prefix"]: resource for resource, details in RESOURCE_DETAILS_MAP.items()} - - -def resource_name(root_dag_id: str, resource: str) -> str: - """Return the resource name for a DAG id.""" - if root_dag_id in RESOURCE_DETAILS_MAP.keys(): - return root_dag_id - if root_dag_id.startswith(tuple(PREFIX_RESOURCES_MAP.keys())): - return root_dag_id - return f"{RESOURCE_DETAILS_MAP[resource]['prefix']}{root_dag_id}" - - -def resource_name_for_dag(root_dag_id: str) -> str: - """ - Return the resource name for a DAG id. - - Note: This function is kept for backwards compatibility. - """ - if root_dag_id == RESOURCE_DAG: - return root_dag_id - if root_dag_id.startswith(RESOURCE_DAG_PREFIX): - return root_dag_id - return f"{RESOURCE_DAG_PREFIX}{root_dag_id}" diff --git a/airflow-core/tests/unit/models/test_dag.py b/airflow-core/tests/unit/models/test_dag.py index 0c7457f587df5..b08e3d8dbe3e9 100644 --- a/airflow-core/tests/unit/models/test_dag.py +++ b/airflow-core/tests/unit/models/test_dag.py @@ -60,6 +60,7 @@ from airflow.models.dagrun import DagRun from airflow.models.serialized_dag import SerializedDagModel from airflow.models.taskinstance import TaskInstance as TI +from airflow.providers.fab.www.security import permissions from airflow.providers.standard.operators.bash import BashOperator from airflow.providers.standard.operators.empty import EmptyOperator from airflow.providers.standard.operators.python import PythonOperator @@ -68,7 +69,6 @@ from airflow.sdk.definitions._internal.templater import NativeEnvironment, SandboxedEnvironment from airflow.sdk.definitions.asset import Asset, AssetAlias, AssetAll, AssetAny from airflow.sdk.definitions.param import Param -from airflow.security import permissions from airflow.timetables.base import DagRunInfo, DataInterval, TimeRestriction, Timetable from airflow.timetables.simple import ( AssetTriggeredTimetable, diff --git a/airflow-core/tests/unit/serialization/test_dag_serialization.py b/airflow-core/tests/unit/serialization/test_dag_serialization.py index d1d2dde78520a..8e963b3231c54 100644 --- a/airflow-core/tests/unit/serialization/test_dag_serialization.py +++ b/airflow-core/tests/unit/serialization/test_dag_serialization.py @@ -62,6 +62,7 @@ from airflow.models.mappedoperator import MappedOperator from airflow.models.xcom import XComModel from airflow.providers.cncf.kubernetes.pod_generator import PodGenerator +from airflow.providers.fab.www.security import permissions from airflow.providers.standard.operators.bash import BashOperator from airflow.providers.standard.sensors.bash import BashSensor from airflow.sdk import AssetAlias, teardown @@ -69,7 +70,6 @@ from airflow.sdk.definitions._internal.expandinput import EXPAND_INPUT_EMPTY from airflow.sdk.definitions.asset import Asset, AssetUniqueKey from airflow.sdk.definitions.param import Param, ParamsDict -from airflow.security import permissions from airflow.serialization.enums import Encoding from airflow.serialization.json_schema import load_dag_schema_dict from airflow.serialization.serialized_objects import ( diff --git a/devel-common/src/tests_common/test_utils/db.py b/devel-common/src/tests_common/test_utils/db.py index 4ab88d1c75e4c..63faf9c9e84d0 100644 --- a/devel-common/src/tests_common/test_utils/db.py +++ b/devel-common/src/tests_common/test_utils/db.py @@ -39,7 +39,6 @@ from airflow.models.dagcode import DagCode from airflow.models.dagwarning import DagWarning from airflow.models.serialized_dag import SerializedDagModel -from airflow.security.permissions import RESOURCE_DAG_PREFIX from airflow.utils.db import add_default_pool_if_not_exists, create_default_connections, reflect_tables from airflow.utils.session import create_session @@ -323,6 +322,11 @@ def clear_dag_specific_permissions(): ) else: raise + try: + from airflow.providers.fab.www.security.permissions import RESOURCE_DAG_PREFIX + except ImportError: + from airflow.security.permissions import RESOURCE_DAG_PREFIX + with create_session() as session: dag_resources = session.query(Resource).filter(Resource.name.like(f"{RESOURCE_DAG_PREFIX}%")).all() dag_resource_ids = [d.id for d in dag_resources] diff --git a/providers/common/compat/src/airflow/providers/common/compat/security/permissions.py b/providers/common/compat/src/airflow/providers/common/compat/security/permissions.py index 1a2d774510e93..ee5bdab31379a 100644 --- a/providers/common/compat/src/airflow/providers/common/compat/security/permissions.py +++ b/providers/common/compat/src/airflow/providers/common/compat/security/permissions.py @@ -16,25 +16,26 @@ # under the License. from __future__ import annotations -from typing import TYPE_CHECKING +from providers.common.compat.src.airflow.providers.common.compat.version_compat import AIRFLOW_V_3_0_PLUS -if TYPE_CHECKING: - from airflow.security.permissions import ( - RESOURCE_ASSET, - RESOURCE_ASSET_ALIAS, - RESOURCE_BACKFILL, - RESOURCE_DAG_VERSION, - ) +# This module is probably only needed for the combination of fab provider 1.5 and airflow 2.x +# It was used during the small window of time when fab provider was based off of main +# after main was version 3 dev and so it was sorta straddling the two versions. +# At that time datasets were renamed assets, but fab provider was still trying to be +# compatible with Airflow 2.x. +# Probably we should not have put this stuff in here but hey. + +if AIRFLOW_V_3_0_PLUS: + # it is not expected that this block would ever be reached + # because only fab provider 2+ is compatible with airflow 3+ + # and fab provider 2+ should use the permissions module in the fab provider + # but we throw it in here just in case. + RESOURCE_ASSET = "Datasets" else: - try: - from airflow.security.permissions import ( - RESOURCE_ASSET, - RESOURCE_ASSET_ALIAS, - RESOURCE_BACKFILL, - RESOURCE_DAG_VERSION, - ) - except ImportError: - from airflow.security.permissions import RESOURCE_DATASET as RESOURCE_ASSET + RESOURCE_ASSET = "Assets" +RESOURCE_ASSET_ALIAS = "Asset Aliases" +RESOURCE_BACKFILL = "Backfills" +RESOURCE_DAG_VERSION = "DAG Versions" __all__ = ["RESOURCE_ASSET", "RESOURCE_ASSET_ALIAS", "RESOURCE_BACKFILL", "RESOURCE_DAG_VERSION"] 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 d6cd3c7f67a3e..7e32438ec23b8 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 @@ -69,6 +69,7 @@ from airflow.providers.fab.www.security import permissions from airflow.providers.fab.www.security.permissions import ( RESOURCE_AUDIT_LOG, + RESOURCE_BACKFILL, RESOURCE_CLUSTER_ACTIVITY, RESOURCE_CONFIG, RESOURCE_CONNECTION, @@ -97,7 +98,6 @@ get_fab_action_from_method_map, get_method_from_fab_action_map, ) -from airflow.security.permissions import RESOURCE_BACKFILL from airflow.utils.session import NEW_SESSION, create_session, provide_session from airflow.utils.yaml import safe_load @@ -116,7 +116,7 @@ RESOURCE_ASSET_ALIAS, ) else: - from airflow.providers.common.compat.security.permissions import ( + from airflow.providers.fab.www.security.permissions import ( RESOURCE_ASSET, RESOURCE_ASSET_ALIAS, ) diff --git a/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py b/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py index 06bebab9f22be..4c5431577a5e4 100644 --- a/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py +++ b/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py @@ -99,12 +99,12 @@ ) from airflow.providers.fab.auth_manager.views.user_stats import CustomUserStatsChartView from airflow.providers.fab.www.security import permissions +from airflow.providers.fab.www.security.permissions import RESOURCE_BACKFILL from airflow.providers.fab.www.security_manager import AirflowSecurityManagerV2 from airflow.providers.fab.www.session import ( AirflowDatabaseSessionInterface, AirflowDatabaseSessionInterface as FabAirflowDatabaseSessionInterface, ) -from airflow.security.permissions import RESOURCE_BACKFILL if TYPE_CHECKING: from airflow.providers.fab.www.security.permissions import ( @@ -112,7 +112,7 @@ RESOURCE_ASSET_ALIAS, ) else: - from airflow.providers.common.compat.security.permissions import ( + from airflow.providers.fab.www.security.permissions import ( RESOURCE_ASSET, RESOURCE_ASSET_ALIAS, ) 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 5e517be3efe30..d5cd046d7f31f 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 @@ -47,18 +47,16 @@ from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager from airflow.providers.fab.auth_manager.security_manager.override import FabAirflowSecurityManagerOverride -from airflow.providers.common.compat.security.permissions import ( - RESOURCE_ASSET, - RESOURCE_ASSET_ALIAS, - RESOURCE_BACKFILL, -) from airflow.providers.fab.www.security.permissions import ( ACTION_CAN_ACCESS_MENU, ACTION_CAN_CREATE, ACTION_CAN_DELETE, ACTION_CAN_EDIT, ACTION_CAN_READ, + RESOURCE_ASSET, + RESOURCE_ASSET_ALIAS, RESOURCE_AUDIT_LOG, + RESOURCE_BACKFILL, RESOURCE_CONFIG, RESOURCE_CONNECTION, RESOURCE_DAG, diff --git a/providers/fab/tests/unit/fab/auth_manager/test_security.py b/providers/fab/tests/unit/fab/auth_manager/test_security.py index 17f5f562b1134..d2907c19a2480 100644 --- a/providers/fab/tests/unit/fab/auth_manager/test_security.py +++ b/providers/fab/tests/unit/fab/auth_manager/test_security.py @@ -37,6 +37,7 @@ from tests_common.test_utils.compat import ignore_provider_compatibility_error from tests_common.test_utils.config import conf_vars +from unit.fab.auth_manager.utils import _resource_name with ignore_provider_compatibility_error("2.9.0+", __file__): from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager @@ -51,7 +52,6 @@ from tests_common.test_utils.asserts import assert_queries_count from tests_common.test_utils.db import clear_db_dags, clear_db_runs from tests_common.test_utils.mock_security_manager import MockSecurityManager -from tests_common.test_utils.permissions import _resource_name from unit.fab.auth_manager.api_endpoints.api_connexion_utils import ( create_user, create_user_scope, @@ -67,7 +67,7 @@ RESOURCE_BACKFILL, ) else: - from airflow.providers.common.compat.security.permissions import ( + from airflow.providers.fab.www.security.permissions import ( RESOURCE_ASSET, RESOURCE_ASSET_ALIAS, RESOURCE_BACKFILL, diff --git a/devel-common/src/tests_common/test_utils/permissions.py b/providers/fab/tests/unit/fab/auth_manager/utils.py similarity index 95% rename from devel-common/src/tests_common/test_utils/permissions.py rename to providers/fab/tests/unit/fab/auth_manager/utils.py index 3abb5d0fbcdab..80068ff8f0b90 100644 --- a/devel-common/src/tests_common/test_utils/permissions.py +++ b/providers/fab/tests/unit/fab/auth_manager/utils.py @@ -16,7 +16,7 @@ # under the License. from __future__ import annotations -from airflow.security import permissions +from airflow.providers.fab.www.security import permissions def _resource_name(dag_id: str, resource_name: str) -> str: