diff --git a/airflow/auth/managers/base_auth_manager.py b/airflow/auth/managers/base_auth_manager.py index f0495f4d9199c..6be53da0807e0 100644 --- a/airflow/auth/managers/base_auth_manager.py +++ b/airflow/auth/managers/base_auth_manager.py @@ -73,18 +73,12 @@ def __init__(self, appbuilder: AirflowAppBuilder) -> None: super().__init__() self.appbuilder = appbuilder - @staticmethod - def get_cli_commands() -> list[CLICommand]: + def init(self) -> None: """ - Vends CLI commands to be included in Airflow CLI. + Run operations when Airflow is initializing. - Override this method to expose commands via Airflow CLI to manage this auth manager. + By default, do nothing. """ - return [] - - def get_api_endpoints(self) -> None | Blueprint: - """Return API endpoint(s) definition for the auth manager.""" - return None def get_user_name(self) -> str: """Return the username associated to the user in session.""" @@ -112,17 +106,26 @@ def get_user_id(self) -> str | None: return str(user_id) return None - def init(self) -> None: - """ - Run operations when Airflow is initializing. - - By default, do nothing. - """ - @abstractmethod def is_logged_in(self) -> bool: """Return whether the user is logged in.""" + @abstractmethod + def get_url_login(self, **kwargs) -> str: + """Return the login page url.""" + + @abstractmethod + def get_url_logout(self) -> str: + """Return the logout page url.""" + + def get_url_user_profile(self) -> str | None: + """ + Return the url to a page displaying info about the current user. + + By default, return None. + """ + return None + @abstractmethod def is_authorized_configuration( self, @@ -413,22 +416,6 @@ def filter_permitted_menu_items(self, menu_items: list[MenuItem]) -> list[MenuIt accessible_items.append(menu_item_copy) return accessible_items - @abstractmethod - def get_url_login(self, **kwargs) -> str: - """Return the login page url.""" - - @abstractmethod - def get_url_logout(self) -> str: - """Return the logout page url.""" - - def get_url_user_profile(self) -> str | None: - """ - Return the url to a page displaying info about the current user. - - By default, return None. - """ - return None - @cached_property def security_manager(self) -> AirflowSecurityManagerV2: """ @@ -443,3 +430,19 @@ def security_manager(self) -> AirflowSecurityManagerV2: from airflow.www.security_manager import AirflowSecurityManagerV2 return AirflowSecurityManagerV2(self.appbuilder) + + @staticmethod + def get_cli_commands() -> list[CLICommand]: + """ + Vends CLI commands to be included in Airflow CLI. + + Override this method to expose commands via Airflow CLI to manage this auth manager. + """ + return [] + + def get_api_endpoints(self) -> None | Blueprint: + """Return API endpoint(s) definition for the auth manager.""" + return None + + def register_views(self) -> None: + """Register views specific to the auth manager.""" diff --git a/airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py b/airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py index 5660ec5d87c02..c8693da3382e0 100644 --- a/airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py +++ b/airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py @@ -36,6 +36,7 @@ from airflow.providers.amazon.aws.auth_manager.security_manager.aws_security_manager_override import ( AwsSecurityManagerOverride, ) +from airflow.providers.amazon.aws.auth_manager.views.auth import AwsAuthManagerAuthenticationViews try: from airflow.auth.managers.base_auth_manager import BaseAuthManager, ResourceMethod @@ -423,6 +424,9 @@ def get_cli_commands() -> list[CLICommand]: ), ] + def register_views(self) -> None: + self.appbuilder.add_view_no_menu(AwsAuthManagerAuthenticationViews()) + @staticmethod def _get_menu_item_request(resource_name: str) -> IsAuthorizedRequest: return { diff --git a/airflow/providers/amazon/aws/auth_manager/security_manager/aws_security_manager_override.py b/airflow/providers/amazon/aws/auth_manager/security_manager/aws_security_manager_override.py index 9d6432f1c6516..0bbd50396e7dc 100644 --- a/airflow/providers/amazon/aws/auth_manager/security_manager/aws_security_manager_override.py +++ b/airflow/providers/amazon/aws/auth_manager/security_manager/aws_security_manager_override.py @@ -27,7 +27,11 @@ class AwsSecurityManagerOverride(AirflowSecurityManagerV2): - """The security manager override specific to AWS auth manager.""" + """ + The security manager override specific to AWS auth manager. + + This class is only used in Airflow 2. This can be safely be removed when min Airflow version >= 3 + """ def register_views(self): """Register views specific to AWS auth manager.""" diff --git a/airflow/providers/fab/auth_manager/fab_auth_manager.py b/airflow/providers/fab/auth_manager/fab_auth_manager.py index ceec5c0e37bd3..85e0687b93b43 100644 --- a/airflow/providers/fab/auth_manager/fab_auth_manager.py +++ b/airflow/providers/fab/auth_manager/fab_auth_manager.py @@ -368,6 +368,9 @@ def get_url_user_profile(self) -> str | None: return None return url_for(f"{self.security_manager.user_view.endpoint}.userinfo") + def register_views(self) -> None: + self.security_manager.register_views() + def _is_authorized( self, *, diff --git a/airflow/www/extensions/init_appbuilder.py b/airflow/www/extensions/init_appbuilder.py index bc5ae117214d6..9f8ef4b602866 100644 --- a/airflow/www/extensions/init_appbuilder.py +++ b/airflow/www/extensions/init_appbuilder.py @@ -342,7 +342,12 @@ def _add_admin_views(self): self.add_view_no_menu(self.indexview) self.add_view_no_menu(UtilView()) self.bm.register_views() - self.sm.register_views() + + try: + get_auth_manager().register_views() + except AttributeError: + # TODO: remove when min airflow version >= 3 + self.sm.register_views() def _add_addon_views(self): """Register declared addons.""" diff --git a/tests/providers/amazon/aws/auth_manager/test_aws_auth_manager.py b/tests/providers/amazon/aws/auth_manager/test_aws_auth_manager.py index 12bd0c13975f1..0ebad2c0fc601 100644 --- a/tests/providers/amazon/aws/auth_manager/test_aws_auth_manager.py +++ b/tests/providers/amazon/aws/auth_manager/test_aws_auth_manager.py @@ -23,6 +23,9 @@ from flask import Flask, session from flask_appbuilder.menu import MenuItem +from airflow.providers.amazon.aws.auth_manager.security_manager.aws_security_manager_override import ( + AwsSecurityManagerOverride, +) from tests.test_utils.compat import AIRFLOW_V_2_8_PLUS, AIRFLOW_V_2_9_PLUS try: @@ -39,7 +42,7 @@ except ImportError: if not AIRFLOW_V_2_8_PLUS: pytest.skip( - "Skipping tests that require AwsSecurityManagerOverride for Airflow < 2.8.0", + "Skipping tests that require airflow.auth.managers.models.resource_details for Airflow < 2.8.0", allow_module_level=True, ) else: @@ -47,9 +50,6 @@ from airflow.providers.amazon.aws.auth_manager.avp.entities import AvpEntities from airflow.providers.amazon.aws.auth_manager.avp.facade import AwsAuthManagerAmazonVerifiedPermissionsFacade from airflow.providers.amazon.aws.auth_manager.aws_auth_manager import AwsAuthManager -from airflow.providers.amazon.aws.auth_manager.security_manager.aws_security_manager_override import ( - AwsSecurityManagerOverride, -) from airflow.providers.amazon.aws.auth_manager.user import AwsAuthManagerUser from airflow.security.permissions import ( RESOURCE_AUDIT_LOG, @@ -785,7 +785,23 @@ def test_security_manager_return_default_security_manager(self, auth_manager_wit def test_get_cli_commands_return_cli_commands(self, auth_manager): assert len(auth_manager.get_cli_commands()) > 0 - @pytest.importorskip("python3-saml") + @pytest.mark.db_test + @patch( + "airflow.providers.amazon.aws.auth_manager.views.auth.conf.get_mandatory_value", return_value="test" + ) + def test_register_views(self, mock_get_mandatory_value, auth_manager_with_appbuilder): + pytest.importorskip("onelogin") + from airflow.providers.amazon.aws.auth_manager.views.auth import AwsAuthManagerAuthenticationViews + + with patch.object(AwsAuthManagerAuthenticationViews, "idp_data"): + auth_manager_with_appbuilder.appbuilder.add_view_no_menu = Mock() + auth_manager_with_appbuilder.register_views() + auth_manager_with_appbuilder.appbuilder.add_view_no_menu.assert_called_once() + assert isinstance( + auth_manager_with_appbuilder.appbuilder.add_view_no_menu.call_args.args[0], + AwsAuthManagerAuthenticationViews, + ) + @pytest.mark.db_test @patch.object(AwsAuthManagerAmazonVerifiedPermissionsFacade, "get_batch_is_authorized_single_result") @patch.object(AwsAuthManagerAmazonVerifiedPermissionsFacade, "get_batch_is_authorized_results")