diff --git a/airflow-core/src/airflow/api_fastapi/app.py b/airflow-core/src/airflow/api_fastapi/app.py index 63acab3b21473..5168013379905 100644 --- a/airflow-core/src/airflow/api_fastapi/app.py +++ b/airflow-core/src/airflow/api_fastapi/app.py @@ -18,12 +18,13 @@ import logging from contextlib import AsyncExitStack, asynccontextmanager -from typing import TYPE_CHECKING, cast +from typing import cast from urllib.parse import urlsplit from fastapi import FastAPI from starlette.routing import Mount +from airflow.api_fastapi.common.auth_manager import init_auth_manager from airflow.api_fastapi.common.dagbag import create_dag_bag from airflow.api_fastapi.core_api.app import ( init_config, @@ -34,12 +35,8 @@ ) from airflow.api_fastapi.execution_api.app import create_task_execution_api_app from airflow.configuration import conf -from airflow.exceptions import AirflowConfigException from airflow.utils.providers_configuration_loader import providers_configuration_loaded -if TYPE_CHECKING: - from airflow.api_fastapi.auth.managers.base_auth_manager import BaseAuthManager - API_BASE_URL = conf.get("api", "base_url", fallback="") if not API_BASE_URL or not API_BASE_URL.endswith("/"): API_BASE_URL += "/" @@ -51,7 +48,6 @@ log = logging.getLogger(__name__) app: FastAPI | None = None -auth_manager: BaseAuthManager | None = None @asynccontextmanager @@ -111,59 +107,9 @@ def cached_app(config=None, testing=False, apps="all") -> FastAPI: def purge_cached_app() -> None: - """Remove the cached version of the app and auth_manager in global state.""" - global app, auth_manager + """Remove the cached version of the app in global state.""" + global app app = None - auth_manager = None - - -def get_auth_manager_cls() -> type[BaseAuthManager]: - """ - Return just the auth manager class without initializing it. - - Useful to save execution time if only static methods need to be called. - """ - auth_manager_cls = conf.getimport(section="core", key="auth_manager") - - if not auth_manager_cls: - raise AirflowConfigException( - "No auth manager defined in the config. Please specify one using section/key [core/auth_manager]." - ) - - return auth_manager_cls - - -def create_auth_manager() -> BaseAuthManager: - """Create the auth manager.""" - global auth_manager - auth_manager_cls = get_auth_manager_cls() - auth_manager = auth_manager_cls() - return auth_manager - - -def init_auth_manager(app: FastAPI | None = None) -> BaseAuthManager: - """Initialize the auth manager.""" - am = create_auth_manager() - am.init() - if app: - app.state.auth_manager = am - - if app and (auth_manager_fastapi_app := am.get_fastapi_app()): - app.mount("/auth", auth_manager_fastapi_app) - - return am - - -def get_auth_manager() -> BaseAuthManager: - """Return the auth manager, provided it's been initialized before.""" - global auth_manager - - if auth_manager is None: - raise RuntimeError( - "Auth Manager has not been initialized yet. " - "The `init_auth_manager` method needs to be called first." - ) - return auth_manager def init_plugins(app: FastAPI) -> None: diff --git a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/services/login.py b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/services/login.py index d4e606ebfc3e0..ac50fe6c967b6 100644 --- a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/services/login.py +++ b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/services/login.py @@ -19,10 +19,10 @@ from fastapi import HTTPException, status -from airflow.api_fastapi.app import get_auth_manager from airflow.api_fastapi.auth.managers.simple.datamodels.login import LoginBody from airflow.api_fastapi.auth.managers.simple.simple_auth_manager import SimpleAuthManager from airflow.api_fastapi.auth.managers.simple.user import SimpleAuthManagerUser +from airflow.api_fastapi.common.auth_manager import AuthManagerDep from airflow.configuration import conf @@ -31,7 +31,9 @@ class SimpleAuthManagerLogin: @staticmethod def create_token( - body: LoginBody, expiration_time_in_seconds: int = conf.getint("api_auth", "jwt_expiration_time") + auth_manager: AuthManagerDep, + body: LoginBody, + expiration_time_in_seconds: int = conf.getint("api_auth", "jwt_expiration_time"), ) -> str: """ Authenticate user with given configuration. @@ -70,9 +72,7 @@ def create_token( role=found_users[0]["role"], ) - return get_auth_manager().generate_jwt( - user=user, expiration_time_in_seconds=expiration_time_in_seconds - ) + return auth_manager.generate_jwt(user=user, expiration_time_in_seconds=expiration_time_in_seconds) @staticmethod def create_token_all_admins( @@ -91,12 +91,11 @@ def create_token_all_admins( @staticmethod def _create_anonymous_admin_user( + auth_manager: AuthManagerDep, expiration_time_in_seconds: int = conf.getint("api_auth", "jwt_expiration_time"), ) -> str: user = SimpleAuthManagerUser( username="Anonymous", role="ADMIN", ) - return get_auth_manager().generate_jwt( - user=user, expiration_time_in_seconds=expiration_time_in_seconds - ) + return auth_manager.generate_jwt(user=user, expiration_time_in_seconds=expiration_time_in_seconds) diff --git a/airflow-core/src/airflow/api_fastapi/common/auth_manager.py b/airflow-core/src/airflow/api_fastapi/common/auth_manager.py new file mode 100644 index 0000000000000..d63a9e0a68253 --- /dev/null +++ b/airflow-core/src/airflow/api_fastapi/common/auth_manager.py @@ -0,0 +1,93 @@ +# 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 TYPE_CHECKING, Annotated + +from fastapi import Depends, Request + +from airflow.api_fastapi.auth.managers.base_auth_manager import BaseAuthManager +from airflow.configuration import conf +from airflow.exceptions import AirflowConfigException + +if TYPE_CHECKING: + from fastapi import FastAPI + +auth_manager: BaseAuthManager | None = None + + +def get_auth_manager_cls() -> type[BaseAuthManager]: + """ + Return just the auth manager class without initializing it. + + Useful to save execution time if only static methods need to be called. + """ + auth_manager_cls = conf.getimport(section="core", key="auth_manager") + + if not auth_manager_cls: + raise AirflowConfigException( + "No auth manager defined in the config. Please specify one using section/key [core/auth_manager]." + ) + + return auth_manager_cls + + +def create_auth_manager() -> BaseAuthManager: + """Create the auth manager.""" + global auth_manager + + auth_manager_cls = get_auth_manager_cls() + auth_manager = auth_manager_cls() + return auth_manager + + +def get_auth_manager() -> BaseAuthManager: + """Return the auth manager, provided it's been initialized before.""" + global auth_manager + + if auth_manager is None: + raise RuntimeError( + "Auth Manager has not been initialized yet. " + "The `init_auth_manager` method needs to be called first." + ) + return auth_manager + + +def init_auth_manager(app: FastAPI | None = None) -> BaseAuthManager: + """Initialize the auth manager for the FastAPI app.""" + am = create_auth_manager() + am.init() + if app: + app.state.auth_manager = am + + if app and (auth_manager_fastapi_app := am.get_fastapi_app()): + app.mount("/auth", auth_manager_fastapi_app) + + return am + + +def dag_auth_manager_from_app(request: Request) -> BaseAuthManager: + """ + FastAPI dependency resolver that returns the shared auth manager instance from app.state. + + This ensures that all API routes using auth manager via dependency injection receive the same + singleton instance that was initialized at app startup. + """ + return request.app.state.auth_manager + + +AuthManagerDep = Annotated[BaseAuthManager, Depends(dag_auth_manager_from_app)] diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/import_error.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/import_error.py index 4beb0ea2cd416..e71aab2bc827d 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/import_error.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/import_error.py @@ -24,11 +24,11 @@ from fastapi import Depends, HTTPException, status from sqlalchemy import select -from airflow.api_fastapi.app import get_auth_manager from airflow.api_fastapi.auth.managers.models.batch_apis import IsAuthorizedDagRequest from airflow.api_fastapi.auth.managers.models.resource_details import ( DagDetails, ) +from airflow.api_fastapi.common.auth_manager import AuthManagerDep from airflow.api_fastapi.common.db.common import ( SessionDep, paginated_select, @@ -67,6 +67,7 @@ def get_import_error( import_error_id: int, session: SessionDep, user: GetUserDep, + auth_manager: AuthManagerDep, ) -> ImportErrorResponse: """Get an import error.""" error = session.scalar(select(ParseImportError).where(ParseImportError.id == import_error_id)) @@ -77,7 +78,6 @@ def get_import_error( ) session.expunge(error) - auth_manager = get_auth_manager() can_read_all_dags = auth_manager.is_authorized_dag(method="GET", user=user) if can_read_all_dags: # Early return if the user has access to all DAGs @@ -128,6 +128,7 @@ def get_import_errors( ], session: SessionDep, user: GetUserDep, + auth_manager: AuthManagerDep, ) -> ImportErrorCollectionResponse: """Get all import errors.""" import_errors_select, total_entries = paginated_select( @@ -138,7 +139,6 @@ def get_import_errors( session=session, ) - auth_manager = get_auth_manager() can_read_all_dags = auth_manager.is_authorized_dag(method="GET", user=user) if can_read_all_dags: # Early return if the user has access to all DAGs diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/auth.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/auth.py index f3c20a9ccef39..854389d70a872 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/auth.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/auth.py @@ -17,7 +17,7 @@ from __future__ import annotations -from airflow.api_fastapi.app import get_auth_manager +from airflow.api_fastapi.common.auth_manager import AuthManagerDep from airflow.api_fastapi.common.router import AirflowRouter from airflow.api_fastapi.core_api.datamodels.ui.auth import ( MenuItemCollectionResponse, @@ -29,10 +29,11 @@ @auth_router.get("/auth/menus") def get_auth_menus( + auth_manager: AuthManagerDep, user: GetUserDep, ) -> MenuItemCollectionResponse: - authorized_menu_items = get_auth_manager().get_authorized_menu_items(user=user) - extra_menu_items = get_auth_manager().get_extra_menu_items(user=user) + authorized_menu_items = auth_manager.get_authorized_menu_items(user=user) + extra_menu_items = auth_manager.get_extra_menu_items(user=user) return MenuItemCollectionResponse( authorized_menu_items=authorized_menu_items, diff --git a/airflow-core/src/airflow/api_fastapi/core_api/security.py b/airflow-core/src/airflow/api_fastapi/core_api/security.py index adc6cf2e01433..c1cde4b93817a 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/security.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/security.py @@ -25,7 +25,6 @@ from jwt import ExpiredSignatureError, InvalidTokenError from pydantic import NonNegativeInt -from airflow.api_fastapi.app import get_auth_manager from airflow.api_fastapi.auth.managers.models.base_user import BaseUser from airflow.api_fastapi.auth.managers.models.resource_details import ( AccessView, @@ -39,6 +38,7 @@ PoolDetails, VariableDetails, ) +from airflow.api_fastapi.common.auth_manager import AuthManagerDep from airflow.api_fastapi.core_api.base import OrmClause from airflow.configuration import conf from airflow.models.dag import DagModel, DagRun, DagTag @@ -49,7 +49,7 @@ if TYPE_CHECKING: from sqlalchemy.sql import Select - from airflow.api_fastapi.auth.managers.base_auth_manager import BaseAuthManager, ResourceMethod + from airflow.api_fastapi.auth.managers.base_auth_manager import ResourceMethod auth_description = ( "To authenticate Airflow API requests, clients must include a JWT (JSON Web Token) in " @@ -64,9 +64,12 @@ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token", description=auth_description) -async def get_user(token_str: Annotated[str, Depends(oauth2_scheme)]) -> BaseUser: +async def get_user( + auth_manager: AuthManagerDep, + token_str: Annotated[str, Depends(oauth2_scheme)], +) -> BaseUser: try: - return await get_auth_manager().get_user_from_token(token_str) + return await auth_manager.get_user_from_token(token_str) except ExpiredSignatureError: raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Token Expired") except InvalidTokenError: @@ -98,12 +101,13 @@ def requires_access_dag( ) -> Callable[[Request, BaseUser], None]: def inner( request: Request, + auth_manager: AuthManagerDep, user: GetUserDep, ) -> None: dag_id: str | None = request.path_params.get("dag_id") _requires_access( - is_authorized_callback=lambda: get_auth_manager().is_authorized_dag( + is_authorized_callback=lambda: auth_manager.is_authorized_dag( method=method, access_entity=access_entity, details=DagDetails(id=dag_id), user=user ) ) @@ -164,10 +168,9 @@ def permitted_dag_filter_factory( """ def depends_permitted_dags_filter( - request: Request, + auth_manager: AuthManagerDep, user: GetUserDep, ) -> PermittedDagFilter: - auth_manager: BaseAuthManager = request.app.state.auth_manager authorized_dags: set[str] = auth_manager.get_authorized_dag_ids(user=user, method=method) return filter_class(authorized_dags) @@ -197,12 +200,13 @@ def depends_permitted_dags_filter( def requires_access_backfill(method: ResourceMethod) -> Callable: def inner( request: Request, - user: Annotated[BaseUser | None, Depends(get_user)] = None, + auth_manager: AuthManagerDep, + user: GetUserDep, ) -> None: backfill_id: NonNegativeInt | None = request.path_params.get("backfill_id") _requires_access( - is_authorized_callback=lambda: get_auth_manager().is_authorized_backfill( + is_authorized_callback=lambda: auth_manager.is_authorized_backfill( method=method, details=BackfillDetails(id=backfill_id), user=user ), ) @@ -213,12 +217,13 @@ def inner( def requires_access_pool(method: ResourceMethod) -> Callable[[Request, BaseUser], None]: def inner( request: Request, + auth_manager: AuthManagerDep, user: GetUserDep, ) -> None: pool_name = request.path_params.get("pool_name") _requires_access( - is_authorized_callback=lambda: get_auth_manager().is_authorized_pool( + is_authorized_callback=lambda: auth_manager.is_authorized_pool( method=method, details=PoolDetails(name=pool_name), user=user ) ) @@ -229,12 +234,13 @@ def inner( def requires_access_connection(method: ResourceMethod) -> Callable[[Request, BaseUser], None]: def inner( request: Request, + auth_manager: AuthManagerDep, user: GetUserDep, ) -> None: connection_id = request.path_params.get("connection_id") _requires_access( - is_authorized_callback=lambda: get_auth_manager().is_authorized_connection( + is_authorized_callback=lambda: auth_manager.is_authorized_connection( method=method, details=ConnectionDetails(conn_id=connection_id), user=user ) ) @@ -245,12 +251,13 @@ def inner( def requires_access_configuration(method: ResourceMethod) -> Callable[[Request, BaseUser | None], None]: def inner( request: Request, - user: Annotated[BaseUser | None, Depends(get_user)] = None, + auth_manager: AuthManagerDep, + user: GetUserDep, ) -> None: section: str | None = request.query_params.get("section") or request.path_params.get("section") _requires_access( - is_authorized_callback=lambda: get_auth_manager().is_authorized_configuration( + is_authorized_callback=lambda: auth_manager.is_authorized_configuration( method=method, details=ConfigurationDetails(section=section), user=user, @@ -263,12 +270,13 @@ def inner( def requires_access_variable(method: ResourceMethod) -> Callable[[Request, BaseUser | None], None]: def inner( request: Request, - user: Annotated[BaseUser | None, Depends(get_user)] = None, + auth_manager: AuthManagerDep, + user: GetUserDep, ) -> None: variable_key: str | None = request.path_params.get("variable_key") _requires_access( - is_authorized_callback=lambda: get_auth_manager().is_authorized_variable( + is_authorized_callback=lambda: auth_manager.is_authorized_variable( method=method, details=VariableDetails(key=variable_key), user=user ), ) @@ -279,12 +287,13 @@ def inner( def requires_access_asset(method: ResourceMethod) -> Callable: def inner( request: Request, - user: Annotated[BaseUser | None, Depends(get_user)] = None, + auth_manager: AuthManagerDep, + user: GetUserDep, ) -> None: asset_id = request.path_params.get("asset_id") _requires_access( - is_authorized_callback=lambda: get_auth_manager().is_authorized_asset( + is_authorized_callback=lambda: auth_manager.is_authorized_asset( method=method, details=AssetDetails(id=asset_id), user=user ), ) @@ -294,11 +303,11 @@ def inner( def requires_access_view(access_view: AccessView) -> Callable[[Request, BaseUser | None], None]: def inner( - request: Request, - user: Annotated[BaseUser | None, Depends(get_user)] = None, + auth_manager: AuthManagerDep, + user: GetUserDep, ) -> None: _requires_access( - is_authorized_callback=lambda: get_auth_manager().is_authorized_view( + is_authorized_callback=lambda: auth_manager.is_authorized_view( access_view=access_view, user=user ), ) @@ -309,12 +318,13 @@ def inner( def requires_access_asset_alias(method: ResourceMethod) -> Callable: def inner( request: Request, - user: Annotated[BaseUser | None, Depends(get_user)] = None, + auth_manager: AuthManagerDep, + user: GetUserDep, ) -> None: asset_alias_id: str | None = request.path_params.get("asset_alias_id") _requires_access( - is_authorized_callback=lambda: get_auth_manager().is_authorized_asset_alias( + is_authorized_callback=lambda: auth_manager.is_authorized_asset_alias( method=method, details=AssetAliasDetails(id=asset_alias_id), user=user ), ) diff --git a/airflow-core/src/airflow/cli/cli_parser.py b/airflow-core/src/airflow/cli/cli_parser.py index a11f6f8aab365..b136e33679dc4 100644 --- a/airflow-core/src/airflow/cli/cli_parser.py +++ b/airflow-core/src/airflow/cli/cli_parser.py @@ -36,7 +36,7 @@ import lazy_object_proxy from rich_argparse import RawTextRichHelpFormatter, RichHelpFormatter -from airflow.api_fastapi.app import get_auth_manager_cls +from airflow.api_fastapi.common.auth_manager import get_auth_manager_cls from airflow.cli.cli_config import ( DAG_CLI_DICT, ActionCommand, diff --git a/airflow-core/src/airflow/cli/commands/standalone_command.py b/airflow-core/src/airflow/cli/commands/standalone_command.py index d6f87210fa920..73981d2c30d70 100644 --- a/airflow-core/src/airflow/cli/commands/standalone_command.py +++ b/airflow-core/src/airflow/cli/commands/standalone_command.py @@ -27,7 +27,7 @@ from termcolor import colored -from airflow.api_fastapi.app import create_auth_manager +from airflow.api_fastapi.common.auth_manager import create_auth_manager from airflow.configuration import conf from airflow.executors import executor_constants from airflow.executors.executor_loader import ExecutorLoader diff --git a/airflow-core/src/airflow/utils/db_manager.py b/airflow-core/src/airflow/utils/db_manager.py index 6483c446f0170..a20f0f2452d2c 100644 --- a/airflow-core/src/airflow/utils/db_manager.py +++ b/airflow-core/src/airflow/utils/db_manager.py @@ -23,7 +23,7 @@ from sqlalchemy import inspect from airflow import settings -from airflow.api_fastapi.app import create_auth_manager +from airflow.api_fastapi.common.auth_manager import get_auth_manager_cls from airflow.configuration import conf from airflow.exceptions import AirflowException from airflow.utils.log.logging_mixin import LoggingMixin @@ -150,7 +150,7 @@ def __init__(self): else: managers = managers_config.split(",") # Add DB manager specified by auth manager (if any) - auth_manager_db_manager = create_auth_manager().get_db_manager() + auth_manager_db_manager = get_auth_manager_cls().get_db_manager() if auth_manager_db_manager and auth_manager_db_manager not in managers: managers.append(auth_manager_db_manager) for module in managers: diff --git a/providers/fab/src/airflow/providers/fab/auth_manager/api/auth/backend/basic_auth.py b/providers/fab/src/airflow/providers/fab/auth_manager/api/auth/backend/basic_auth.py index bf48beff41c9a..c8dc55daeba18 100644 --- a/providers/fab/src/airflow/providers/fab/auth_manager/api/auth/backend/basic_auth.py +++ b/providers/fab/src/airflow/providers/fab/auth_manager/api/auth/backend/basic_auth.py @@ -25,7 +25,7 @@ from flask_appbuilder.const import AUTH_LDAP from flask_login import login_user -from airflow.api_fastapi.app import get_auth_manager +from airflow.api_fastapi.common.auth_manager import get_auth_manager if TYPE_CHECKING: from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager diff --git a/providers/fab/src/airflow/providers/fab/auth_manager/api/auth/backend/kerberos_auth.py b/providers/fab/src/airflow/providers/fab/auth_manager/api/auth/backend/kerberos_auth.py index d419524bf1043..d78e2c86ec8e2 100644 --- a/providers/fab/src/airflow/providers/fab/auth_manager/api/auth/backend/kerberos_auth.py +++ b/providers/fab/src/airflow/providers/fab/auth_manager/api/auth/backend/kerberos_auth.py @@ -26,7 +26,7 @@ from flask import Response, current_app, g, make_response, request from requests_kerberos import HTTPKerberosAuth -from airflow.api_fastapi.app import get_auth_manager +from airflow.api_fastapi.common.auth_manager import get_auth_manager from airflow.configuration import conf from airflow.utils.net import getfqdn diff --git a/providers/fab/src/airflow/providers/fab/auth_manager/api/auth/backend/session.py b/providers/fab/src/airflow/providers/fab/auth_manager/api/auth/backend/session.py index 8b39c0e8f775c..e8347407823fc 100644 --- a/providers/fab/src/airflow/providers/fab/auth_manager/api/auth/backend/session.py +++ b/providers/fab/src/airflow/providers/fab/auth_manager/api/auth/backend/session.py @@ -23,7 +23,7 @@ from flask import Response -from airflow.api_fastapi.app import get_auth_manager +from airflow.api_fastapi.common.auth_manager import get_auth_manager CLIENT_AUTH: tuple[str, str] | Any | None = None diff --git a/providers/fab/src/airflow/providers/fab/auth_manager/api_endpoints/role_and_permission_endpoint.py b/providers/fab/src/airflow/providers/fab/auth_manager/api_endpoints/role_and_permission_endpoint.py index e8aacedea8c83..457ff31d16713 100644 --- a/providers/fab/src/airflow/providers/fab/auth_manager/api_endpoints/role_and_permission_endpoint.py +++ b/providers/fab/src/airflow/providers/fab/auth_manager/api_endpoints/role_and_permission_endpoint.py @@ -24,7 +24,7 @@ from marshmallow import ValidationError from sqlalchemy import asc, desc, func, select -from airflow.api_fastapi.app import get_auth_manager +from airflow.api_fastapi.common.auth_manager import get_auth_manager from airflow.providers.fab.auth_manager.models import Action, Role from airflow.providers.fab.auth_manager.schemas.role_and_permission_schema import ( ActionCollection, diff --git a/providers/fab/src/airflow/providers/fab/auth_manager/api_endpoints/user_endpoint.py b/providers/fab/src/airflow/providers/fab/auth_manager/api_endpoints/user_endpoint.py index 8c504d6446694..6976358bca54b 100644 --- a/providers/fab/src/airflow/providers/fab/auth_manager/api_endpoints/user_endpoint.py +++ b/providers/fab/src/airflow/providers/fab/auth_manager/api_endpoints/user_endpoint.py @@ -25,7 +25,7 @@ from sqlalchemy import asc, desc, func, select from werkzeug.security import generate_password_hash -from airflow.api_fastapi.app import get_auth_manager +from airflow.api_fastapi.common.auth_manager import get_auth_manager from airflow.providers.fab.auth_manager.models import User from airflow.providers.fab.auth_manager.schemas.user_schema import ( UserCollection, diff --git a/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/services/login.py b/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/services/login.py index 0792efd18ce46..5433be11c032c 100644 --- a/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/services/login.py +++ b/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/services/login.py @@ -21,7 +21,7 @@ from starlette import status from starlette.exceptions import HTTPException -from airflow.api_fastapi.app import get_auth_manager +from airflow.api_fastapi.common.auth_manager import get_auth_manager from airflow.configuration import conf from airflow.providers.fab.auth_manager.api_fastapi.datamodels.login import LoginBody, LoginResponse diff --git a/providers/fab/src/airflow/providers/fab/www/api_connexion/security.py b/providers/fab/src/airflow/providers/fab/www/api_connexion/security.py index f238e9de9b356..ad443e07bd18a 100644 --- a/providers/fab/src/airflow/providers/fab/www/api_connexion/security.py +++ b/providers/fab/src/airflow/providers/fab/www/api_connexion/security.py @@ -21,7 +21,7 @@ from flask import Response, current_app -from airflow.api_fastapi.app import get_auth_manager +from airflow.api_fastapi.common.auth_manager import get_auth_manager from airflow.providers.fab.www.api_connexion.exceptions import PermissionDenied, Unauthenticated if TYPE_CHECKING: diff --git a/providers/fab/src/airflow/providers/fab/www/app.py b/providers/fab/src/airflow/providers/fab/www/app.py index 873e7adb8f048..c0e8b9c841535 100644 --- a/providers/fab/src/airflow/providers/fab/www/app.py +++ b/providers/fab/src/airflow/providers/fab/www/app.py @@ -26,7 +26,7 @@ from sqlalchemy.engine.url import make_url from airflow import settings -from airflow.api_fastapi.app import get_auth_manager +from airflow.api_fastapi.common.auth_manager import get_auth_manager from airflow.configuration import conf from airflow.exceptions import AirflowConfigException from airflow.logging_config import configure_logging diff --git a/providers/fab/src/airflow/providers/fab/www/auth.py b/providers/fab/src/airflow/providers/fab/www/auth.py index 0ac83def61986..25c856e6017f0 100644 --- a/providers/fab/src/airflow/providers/fab/www/auth.py +++ b/providers/fab/src/airflow/providers/fab/www/auth.py @@ -30,7 +30,6 @@ PERMISSION_PREFIX, ) -from airflow.api_fastapi.app import get_auth_manager from airflow.api_fastapi.auth.managers.models.resource_details import ( AccessView, ConnectionDetails, @@ -39,6 +38,7 @@ PoolDetails, VariableDetails, ) +from airflow.api_fastapi.common.auth_manager import get_auth_manager from airflow.configuration import conf from airflow.providers.fab.www.utils import get_fab_auth_manager from airflow.utils.net import get_hostname diff --git a/providers/fab/src/airflow/providers/fab/www/extensions/init_appbuilder.py b/providers/fab/src/airflow/providers/fab/www/extensions/init_appbuilder.py index 5776d2b2aff6a..d75ef9c32df3e 100644 --- a/providers/fab/src/airflow/providers/fab/www/extensions/init_appbuilder.py +++ b/providers/fab/src/airflow/providers/fab/www/extensions/init_appbuilder.py @@ -39,7 +39,7 @@ from flask_appbuilder.views import IndexView, UtilView from airflow import settings -from airflow.api_fastapi.app import create_auth_manager, get_auth_manager +from airflow.api_fastapi.common.auth_manager import create_auth_manager, get_auth_manager from airflow.configuration import conf from airflow.providers.fab.www.security_manager import AirflowSecurityManagerV2 from airflow.providers.fab.www.views import FabIndexView diff --git a/providers/fab/src/airflow/providers/fab/www/extensions/init_jinja_globals.py b/providers/fab/src/airflow/providers/fab/www/extensions/init_jinja_globals.py index 5929a40d8f576..d6672aa5b755b 100644 --- a/providers/fab/src/airflow/providers/fab/www/extensions/init_jinja_globals.py +++ b/providers/fab/src/airflow/providers/fab/www/extensions/init_jinja_globals.py @@ -21,7 +21,7 @@ import pendulum import airflow -from airflow.api_fastapi.app import get_auth_manager +from airflow.api_fastapi.common.auth_manager import get_auth_manager from airflow.configuration import conf from airflow.utils.net import get_hostname from airflow.utils.platform import get_airflow_git_version diff --git a/providers/fab/src/airflow/providers/fab/www/extensions/init_views.py b/providers/fab/src/airflow/providers/fab/www/extensions/init_views.py index 76883ae3ad891..c80e0005aaf12 100644 --- a/providers/fab/src/airflow/providers/fab/www/extensions/init_views.py +++ b/providers/fab/src/airflow/providers/fab/www/extensions/init_views.py @@ -25,7 +25,7 @@ from connexion.exceptions import BadRequestProblem, ProblemException from flask import request -from airflow.api_fastapi.app import get_auth_manager +from airflow.api_fastapi.common.auth_manager import get_auth_manager from airflow.providers.fab.www.api_connexion.exceptions import common_error_handler if TYPE_CHECKING: diff --git a/providers/fab/src/airflow/providers/fab/www/security_manager.py b/providers/fab/src/airflow/providers/fab/www/security_manager.py index 7f3e4ef262035..6ddfc9a2ffc3c 100644 --- a/providers/fab/src/airflow/providers/fab/www/security_manager.py +++ b/providers/fab/src/airflow/providers/fab/www/security_manager.py @@ -22,7 +22,7 @@ from flask_limiter import Limiter from flask_limiter.util import get_remote_address -from airflow.api_fastapi.app import get_auth_manager +from airflow.api_fastapi.common.auth_manager import get_auth_manager from airflow.providers.fab.www.utils import CustomSQLAInterface, get_method_from_fab_action_map from airflow.utils.log.logging_mixin import LoggingMixin diff --git a/providers/fab/src/airflow/providers/fab/www/utils.py b/providers/fab/src/airflow/providers/fab/www/utils.py index 3bde1300ec8a8..4d43d4b0aa65b 100644 --- a/providers/fab/src/airflow/providers/fab/www/utils.py +++ b/providers/fab/src/airflow/providers/fab/www/utils.py @@ -28,7 +28,7 @@ from sqlalchemy import types from sqlalchemy.ext.associationproxy import AssociationProxy -from airflow.api_fastapi.app import get_auth_manager +from airflow.api_fastapi.common.auth_manager import get_auth_manager from airflow.configuration import conf from airflow.providers.fab.www.security.permissions import ( ACTION_CAN_ACCESS_MENU, diff --git a/providers/fab/src/airflow/providers/fab/www/views.py b/providers/fab/src/airflow/providers/fab/www/views.py index 87a801665fcbe..ef5d64d155bbb 100644 --- a/providers/fab/src/airflow/providers/fab/www/views.py +++ b/providers/fab/src/airflow/providers/fab/www/views.py @@ -29,8 +29,8 @@ ) from flask_appbuilder import IndexView, expose -from airflow.api_fastapi.app import get_auth_manager from airflow.api_fastapi.auth.managers.base_auth_manager import COOKIE_NAME_JWT_TOKEN +from airflow.api_fastapi.common.auth_manager import get_auth_manager from airflow.configuration import conf # Following the release of https://github.com/python/cpython/issues/102153 in Python 3.9.17 on diff --git a/providers/fab/tests/unit/fab/auth_manager/api_endpoints/remote_user_api_auth_backend.py b/providers/fab/tests/unit/fab/auth_manager/api_endpoints/remote_user_api_auth_backend.py index 247bb168fd042..ac517d8db049a 100644 --- a/providers/fab/tests/unit/fab/auth_manager/api_endpoints/remote_user_api_auth_backend.py +++ b/providers/fab/tests/unit/fab/auth_manager/api_endpoints/remote_user_api_auth_backend.py @@ -26,7 +26,7 @@ from flask import Response, request from flask_login import login_user -from airflow.api_fastapi.app import get_auth_manager +from airflow.api_fastapi.common.auth_manager import get_auth_manager if TYPE_CHECKING: from requests.auth import AuthBase 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..e4dd378d9eeb4 100644 --- a/providers/fab/tests/unit/fab/auth_manager/test_security.py +++ b/providers/fab/tests/unit/fab/auth_manager/test_security.py @@ -43,7 +43,7 @@ from airflow.providers.fab.auth_manager.models import assoc_permission_role from airflow.providers.fab.auth_manager.models.anonymous_user import AnonymousUser -from airflow.api_fastapi.app import get_auth_manager +from airflow.api_fastapi.common.auth_manager import get_auth_manager from airflow.providers.fab.www import app as application from airflow.providers.fab.www.security import permissions from airflow.providers.fab.www.security.permissions import ACTION_CAN_READ