Skip to content

Commit

Permalink
Do not use core Airflow Flask related resources in FAB provider (pack…
Browse files Browse the repository at this point in the history
…age `security`) (#45471)
  • Loading branch information
vincbeck authored Jan 8, 2025
1 parent 97b4ea6 commit a283841
Show file tree
Hide file tree
Showing 33 changed files with 177 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from airflow.providers.fab.www.api_connexion.exceptions import AlreadyExists, BadRequest, NotFound
from airflow.providers.fab.www.api_connexion.parameters import check_limit, format_parameters
from airflow.providers.fab.www.api_connexion.security import requires_access_custom_view
from airflow.security import permissions
from airflow.providers.fab.www.security import permissions

if TYPE_CHECKING:
from airflow.providers.fab.auth_manager.security_manager.override import FabAirflowSecurityManagerOverride
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from airflow.providers.fab.www.api_connexion.exceptions import AlreadyExists, BadRequest, NotFound, Unknown
from airflow.providers.fab.www.api_connexion.parameters import check_limit, format_parameters
from airflow.providers.fab.www.api_connexion.security import requires_access_custom_view
from airflow.security import permissions
from airflow.providers.fab.www.security import permissions

if TYPE_CHECKING:
from airflow.providers.fab.auth_manager.models import Role
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@
from airflow.providers.fab.www.app import create_app
from airflow.providers.fab.www.constants import SWAGGER_BUNDLE, SWAGGER_ENABLED
from airflow.providers.fab.www.extensions.init_views import _CustomErrorRequestBodyValidator, _LazyResolver
from airflow.security import permissions
from airflow.security.permissions import (
from airflow.providers.fab.www.security import permissions
from airflow.providers.fab.www.security.permissions import (
ACTION_CAN_ACCESS_MENU,
RESOURCE_AUDIT_LOG,
RESOURCE_CLUSTER_ACTIVITY,
Expand Down Expand Up @@ -101,7 +101,7 @@
from airflow.providers.common.compat.assets import AssetDetails
from airflow.providers.fab.auth_manager.security_manager.override import FabAirflowSecurityManagerOverride
from airflow.providers.fab.www.extensions.init_appbuilder import AirflowAppBuilder
from airflow.security.permissions import RESOURCE_ASSET
from airflow.providers.fab.www.security.permissions import RESOURCE_ASSET
else:
from airflow.providers.common.compat.security.permissions import RESOURCE_ASSET

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,15 @@
CustomUserInfoEditView,
)
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_manager import AirflowSecurityManagerV2
from airflow.providers.fab.www.session import (
AirflowDatabaseSessionInterface as FabAirflowDatabaseSessionInterface,
)
from airflow.security import permissions
from airflow.www.session import AirflowDatabaseSessionInterface

if TYPE_CHECKING:
from airflow.security.permissions import RESOURCE_ASSET
from airflow.providers.fab.www.security.permissions import RESOURCE_ASSET
else:
from airflow.providers.common.compat.security.permissions import RESOURCE_ASSET

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
)
from flask_babel import lazy_gettext

from airflow.security import permissions
from airflow.providers.fab.www.security import permissions


class ActionModelView(PermissionModelView):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from flask_appbuilder.security.views import RoleModelView

from airflow.security import permissions
from airflow.providers.fab.www.security import permissions


class CustomRoleModelView(RoleModelView):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
UserRemoteUserModelView,
)

from airflow.security import permissions
from airflow.providers.fab.www.security import permissions


class MultiResourceUserMixin:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
UserInfoEditView,
)

from airflow.security import permissions
from airflow.providers.fab.www.security import permissions


class CustomUserInfoEditView(UserInfoEditView):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from flask_appbuilder.security.views import UserStatsChartView

from airflow.security import permissions
from airflow.providers.fab.www.security import permissions


class CustomUserStatsChartView(UserStatsChartView):
Expand Down
17 changes: 17 additions & 0 deletions providers/src/airflow/providers/fab/www/security/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#
# 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.
123 changes: 123 additions & 0 deletions providers/src/airflow/providers/fab/www/security/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# 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_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_WARNING = "DAG Warnings"
RESOURCE_CLUSTER_ACTIVITY = "Cluster Activity"
RESOURCE_ASSET = "Assets"
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.

Note that since a sub-DAG should follow the permission of its
parent DAG, you should pass ``DagModel.root_dag_id`` to this function,
for a subdag. A normal dag should pass the ``DagModel.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 that since a sub-DAG should follow the permission of its
parent DAG, you should pass ``DagModel.root_dag_id`` to this function,
for a subdag. A normal dag should pass the ``DagModel.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}"
4 changes: 2 additions & 2 deletions providers/src/airflow/providers/fab/www/security_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@
)
from airflow.exceptions import AirflowException
from airflow.models import Connection, DagRun, Pool, TaskInstance, Variable
from airflow.providers.fab.www.utils import CustomSQLAInterface
from airflow.security.permissions import (
from airflow.providers.fab.www.security.permissions import (
RESOURCE_ADMIN_MENU,
RESOURCE_ASSET,
RESOURCE_AUDIT_LOG,
Expand All @@ -64,6 +63,7 @@
RESOURCE_VARIABLE,
RESOURCE_XCOM,
)
from airflow.providers.fab.www.utils import CustomSQLAInterface
from airflow.utils.log.logging_mixin import LoggingMixin

EXISTING_ROLES = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import time_machine

from airflow.providers.fab.www.api_connexion.exceptions import EXCEPTIONS_LINK_MAP
from airflow.security import permissions
from airflow.providers.fab.www.security import permissions
from airflow.utils import timezone

from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import create_user, delete_user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

from airflow.models import DagModel
from airflow.providers.fab.www.api_connexion.exceptions import EXCEPTIONS_LINK_MAP
from airflow.security import permissions
from airflow.providers.fab.www.security import permissions
from airflow.utils.session import provide_session

from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import create_user, delete_user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from airflow.models.dag import DAG, DagModel
from airflow.models.dagrun import DagRun
from airflow.models.param import Param
from airflow.security import permissions
from airflow.providers.fab.www.security import permissions
from airflow.utils import timezone
from airflow.utils.session import create_session
from airflow.utils.state import DagRunState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import pytest

from airflow.models import DagBag
from airflow.security import permissions
from airflow.providers.fab.www.security import permissions

from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import create_user, delete_user
from tests_common.test_utils.db import clear_db_dag_code, clear_db_dags, clear_db_serialized_dags
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from airflow.models.dag import DagModel
from airflow.models.dagwarning import DagWarning
from airflow.security import permissions
from airflow.providers.fab.www.security import permissions
from airflow.utils.session import create_session

from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import create_user, delete_user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import pytest

from airflow.models import Log
from airflow.security import permissions
from airflow.providers.fab.www.security import permissions
from airflow.utils import timezone

from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import create_user, delete_user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import pytest

from airflow.models.dag import DagModel
from airflow.security import permissions
from airflow.providers.fab.www.security import permissions
from airflow.utils import timezone

from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import create_user, delete_user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import pytest

from airflow.providers.fab.www.api_connexion.exceptions import EXCEPTIONS_LINK_MAP
from airflow.security import permissions
from airflow.providers.fab.www.security import permissions

from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import (
create_role,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

from airflow.models import DagRun, TaskInstance
from airflow.providers.fab.www.api_connexion.exceptions import EXCEPTIONS_LINK_MAP
from airflow.security import permissions
from airflow.providers.fab.www.security import permissions
from airflow.utils.session import provide_session
from airflow.utils.state import State
from airflow.utils.timezone import datetime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from sqlalchemy.sql.functions import count

from airflow.providers.fab.www.api_connexion.exceptions import EXCEPTIONS_LINK_MAP
from airflow.security import permissions
from airflow.providers.fab.www.security import permissions
from airflow.utils import timezone
from airflow.utils.session import create_session

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import pytest

from airflow.models import Variable
from airflow.security import permissions
from airflow.providers.fab.www.security import permissions

from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import create_user, delete_user
from tests_common.test_utils.db import clear_db_variables
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from airflow.models.taskinstance import TaskInstance
from airflow.models.xcom import BaseXCom, XCom
from airflow.operators.empty import EmptyOperator
from airflow.security import permissions
from airflow.providers.fab.www.security import permissions
from airflow.utils import timezone
from airflow.utils.session import create_session
from airflow.utils.types import DagRunType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from airflow.providers.fab.auth_manager.cli_commands import role_command
from airflow.providers.fab.auth_manager.cli_commands.utils import get_application_builder

from airflow.security import permissions
from airflow.providers.fab.www.security import permissions

pytestmark = pytest.mark.db_test

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
role_collection_schema,
role_schema,
)
from airflow.security import permissions
from airflow.providers.fab.www.security import permissions

from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import create_role, delete_role

Expand Down
2 changes: 1 addition & 1 deletion providers/tests/fab/auth_manager/test_fab_auth_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
from airflow.providers.fab.auth_manager.security_manager.override import FabAirflowSecurityManagerOverride

from airflow.providers.common.compat.security.permissions import RESOURCE_ASSET
from airflow.security.permissions import (
from airflow.providers.fab.www.security.permissions import (
ACTION_CAN_ACCESS_MENU,
ACTION_CAN_CREATE,
ACTION_CAN_DELETE,
Expand Down
Loading

0 comments on commit a283841

Please sign in to comment.