This is an automated email from the ASF dual-hosted git repository. ferruzzi pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push: new 2b0d88e450 Handle logout by auth manager (#32819) 2b0d88e450 is described below commit 2b0d88e450f11af8e447864ca258142a6756126d Author: Vincent <97131062+vincb...@users.noreply.github.com> AuthorDate: Mon Jul 31 15:20:38 2023 -0400 Handle logout by auth manager (#32819) * Handle logout by auth manager --- airflow/auth/managers/base_auth_manager.py | 10 ++++------ airflow/auth/managers/fab/fab_auth_manager.py | 6 ++++++ airflow/www/auth.py | 2 +- airflow/www/extensions/init_appbuilder.py | 4 ---- airflow/www/extensions/init_security.py | 4 +--- airflow/www/templates/appbuilder/navbar_right.html | 2 +- tests/auth/managers/fab/test_fab_auth_manager.py | 11 +++++++++++ tests/www/views/test_session.py | 8 +------- 8 files changed, 25 insertions(+), 22 deletions(-) diff --git a/airflow/auth/managers/base_auth_manager.py b/airflow/auth/managers/base_auth_manager.py index a234efcaca..cb40ee9c87 100644 --- a/airflow/auth/managers/base_auth_manager.py +++ b/airflow/auth/managers/base_auth_manager.py @@ -41,32 +41,30 @@ class BaseAuthManager(LoggingMixin): @abstractmethod def get_user_name(self) -> str: """Return the username associated to the user in session.""" - ... @abstractmethod def get_user(self) -> BaseUser: """Return the user associated to the user in session.""" - ... @abstractmethod def get_user_id(self) -> str: """Return the user ID associated to the user in session.""" - ... @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.""" @abstractmethod def get_url_user_profile(self) -> str | None: """Return the url to a page displaying info about the current user.""" - ... def get_security_manager_override_class(self) -> type: """ diff --git a/airflow/auth/managers/fab/fab_auth_manager.py b/airflow/auth/managers/fab/fab_auth_manager.py index 085b6e997b..7a23953b35 100644 --- a/airflow/auth/managers/fab/fab_auth_manager.py +++ b/airflow/auth/managers/fab/fab_auth_manager.py @@ -70,6 +70,12 @@ class FabAuthManager(BaseAuthManager): else: return url_for(f"{self.security_manager.auth_view.endpoint}.login") + def get_url_logout(self): + """Return the logout page url.""" + if not self.security_manager.auth_view: + raise AirflowException("`auth_view` not defined in the security manager.") + return url_for(f"{self.security_manager.auth_view.endpoint}.logout") + def get_url_user_profile(self) -> str | None: """Return the url to a page displaying info about the current user.""" if not self.security_manager.user_view: diff --git a/airflow/www/auth.py b/airflow/www/auth.py index 46b645f197..9afe995054 100644 --- a/airflow/www/auth.py +++ b/airflow/www/auth.py @@ -54,7 +54,7 @@ def has_access(permissions: Sequence[tuple[str, str]] | None = None) -> Callable hostname=get_hostname() if conf.getboolean("webserver", "EXPOSE_HOSTNAME") else "redact", - logout_url=appbuilder.get_url_for_logout, + logout_url=get_auth_manager().get_url_logout(), ), 403, ) diff --git a/airflow/www/extensions/init_appbuilder.py b/airflow/www/extensions/init_appbuilder.py index 2537ea8bc7..11c358abb6 100644 --- a/airflow/www/extensions/init_appbuilder.py +++ b/airflow/www/extensions/init_appbuilder.py @@ -588,10 +588,6 @@ class AirflowAppBuilder: def get_url_for_login_with(self, next_url: str | None = None) -> str: return get_auth_manager().get_url_login(next_url=next_url) - @property - def get_url_for_logout(self): - return url_for(f"{self.sm.auth_view.endpoint}.logout") - @property def get_url_for_index(self): return url_for(f"{self.indexview.endpoint}.{self.indexview.default_view}") diff --git a/airflow/www/extensions/init_security.py b/airflow/www/extensions/init_security.py index cdd4ec8862..ea3c2211c9 100644 --- a/airflow/www/extensions/init_security.py +++ b/airflow/www/extensions/init_security.py @@ -20,7 +20,6 @@ import logging from importlib import import_module from flask import g, redirect -from flask_login import logout_user from airflow.configuration import conf from airflow.exceptions import AirflowConfigException, AirflowException @@ -70,5 +69,4 @@ def init_check_user_active(app): @app.before_request def check_user_active(): if get_auth_manager().is_logged_in() and not g.user.is_active: - logout_user() - return redirect(get_auth_manager().get_url_login()) + return redirect(get_auth_manager().get_url_logout()) diff --git a/airflow/www/templates/appbuilder/navbar_right.html b/airflow/www/templates/appbuilder/navbar_right.html index 2c16e09d96..8eec9f9fcf 100644 --- a/airflow/www/templates/appbuilder/navbar_right.html +++ b/airflow/www/templates/appbuilder/navbar_right.html @@ -78,7 +78,7 @@ <li><a href="{{user_profile_url}}"><span class="material-icons">account_circle</span>{{_("Your Profile")}}</a></li> <li role="separator" class="divider"></li> {% endif %} - <li><a href="{{appbuilder.get_url_for_logout}}"><span class="material-icons">exit_to_app</span>{{_("Log Out")}}</a></li> + <li><a href="{{auth_manager.get_url_logout()}}"><span class="material-icons">exit_to_app</span>{{_("Log Out")}}</a></li> </ul> </li> {% else %} diff --git a/tests/auth/managers/fab/test_fab_auth_manager.py b/tests/auth/managers/fab/test_fab_auth_manager.py index 96ddbc7f3e..c36ef8686d 100644 --- a/tests/auth/managers/fab/test_fab_auth_manager.py +++ b/tests/auth/managers/fab/test_fab_auth_manager.py @@ -99,6 +99,17 @@ class TestFabAuthManager: auth_manager.get_url_login(next_url="next_url") mock_url_for.assert_called_once_with("test_endpoint.login", next="next_url") + def test_get_url_logout_when_auth_view_not_defined(self, auth_manager): + with pytest.raises(AirflowException, match="`auth_view` not defined in the security manager."): + auth_manager.get_url_logout() + + @mock.patch("airflow.auth.managers.fab.fab_auth_manager.url_for") + def test_get_url_logout(self, mock_url_for, auth_manager): + auth_manager.security_manager.auth_view = Mock() + auth_manager.security_manager.auth_view.endpoint = "test_endpoint" + auth_manager.get_url_logout() + mock_url_for.assert_called_once_with("test_endpoint.logout") + def test_get_url_user_profile_when_auth_view_not_defined(self, auth_manager): assert auth_manager.get_url_user_profile() is None diff --git a/tests/www/views/test_session.py b/tests/www/views/test_session.py index d8987a0178..822cf967fd 100644 --- a/tests/www/views/test_session.py +++ b/tests/www/views/test_session.py @@ -95,10 +95,4 @@ def test_check_active_user(app, user_client): user.active = False resp = user_client.get("/home") assert resp.status_code == 302 - assert "/login" in resp.headers.get("Location") - - # And they were logged out - user.active = True - resp = user_client.get("/home") - assert resp.status_code == 302 - assert "/login" in resp.headers.get("Location") + assert "/logout" in resp.headers.get("Location")