diff --git a/lms/envs/common.py b/lms/envs/common.py index e3e486108f60..95c44fc3ff8a 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -1215,6 +1215,18 @@ TPA_PROVIDER_BURST_THROTTLE = '10/min' TPA_PROVIDER_SUSTAINED_THROTTLE = '50/hr' +# .. toggle_name: TPA_AUTOMATIC_LOGOUT_ENABLED +# .. toggle_implementation: DjangoSetting +# .. toggle_default: False +# .. toggle_description: Redirect the user to the TPA logout URL if this flag is enabled, the +# TPA logout URL is configured, and the user logs in through TPA. +# .. toggle_use_cases: opt_in +# .. toggle_warning: Enabling this toggle skips rendering logout.html, which is used to log the user out +# from the different IDAs. To ensure the user is logged out of all the IDAs be sure to redirect +# back to /logout after logging out of the TPA. +# .. toggle_creation_date: 2023-05-07 +TPA_AUTOMATIC_LOGOUT_ENABLED = False + ################################## TEMPLATE CONFIGURATION ##################################### # Mako templating import tempfile # pylint: disable=wrong-import-position,wrong-import-order diff --git a/openedx/core/djangoapps/user_authn/views/logout.py b/openedx/core/djangoapps/user_authn/views/logout.py index c00fa2a5dada..13301f5e3bb1 100644 --- a/openedx/core/djangoapps/user_authn/views/logout.py +++ b/openedx/core/djangoapps/user_authn/views/logout.py @@ -8,6 +8,7 @@ import bleach from django.conf import settings from django.contrib.auth import logout +from django.shortcuts import redirect from django.utils.http import urlencode from django.views.generic import TemplateView from oauth2_provider.models import Application @@ -83,6 +84,17 @@ def dispatch(self, request, *args, **kwargs): delete_logged_in_cookies(response) mark_user_change_as_expected(None) + + # Redirect to tpa_logout_url if TPA_AUTOMATIC_LOGOUT_ENABLED is set to True and if + # tpa_logout_url is configured. + # + # NOTE: This step skips rendering logout.html, which is used to log the user out from the + # different IDAs. To ensure the user is logged out of all the IDAs be sure to redirect + # back to /logout after logging out of the TPA. + if getattr(settings, 'TPA_AUTOMATIC_LOGOUT_ENABLED', False): + if self.tpa_logout_url: + return redirect(self.tpa_logout_url) + return response def _build_logout_url(self, url): @@ -113,13 +125,18 @@ def _is_enterprise_target(self, url): def _show_tpa_logout_link(self, target, referrer): """ Return Boolean value indicating if TPA logout link needs to displayed or not. - We display TPA logout link when user has active SSO session and logout flow is - triggered via learner portal. + We display TPA logout link when user has active SSO session, logout flow is + triggered via learner portal and TPA_AUTOMATIC_LOGOUT_ENABLED toggle is False. Args: target: url of the page to land after logout referrer: url of the page where logout request initiated """ - if bool(target == self.default_target and self.tpa_logout_url) and settings.LEARNER_PORTAL_URL_ROOT in referrer: + tpa_automatic_logout_enabled = getattr(settings, 'TPA_AUTOMATIC_LOGOUT_ENABLED', False) + if ( + bool(target == self.default_target and self.tpa_logout_url) and + settings.LEARNER_PORTAL_URL_ROOT in referrer and + not tpa_automatic_logout_enabled + ): return True return False diff --git a/openedx/core/djangoapps/user_authn/views/tests/test_logout.py b/openedx/core/djangoapps/user_authn/views/tests/test_logout.py index 139491d03d99..7d10fe1021ef 100644 --- a/openedx/core/djangoapps/user_authn/views/tests/test_logout.py +++ b/openedx/core/djangoapps/user_authn/views/tests/test_logout.py @@ -196,6 +196,33 @@ def test_learner_portal_logout_having_idp_logout_url(self): } self.assertDictContainsSubset(expected, response.context_data) + @mock.patch('django.conf.settings.TPA_AUTOMATIC_LOGOUT_ENABLED', True) + def test_automatic_tpa_logout_url_redirect(self): + """ + Test user automatically redirected to tpa logout_url + when TPA_AUTOMATIC_LOGOUT is set to True. + """ + idp_logout_url = 'http://mock-idp.com/logout' + client = self._create_oauth_client() + + with mock.patch( + 'openedx.core.djangoapps.user_authn.views.logout.tpa_pipeline.get_idp_logout_url_from_running_pipeline' + ) as mock_idp_logout_url: + mock_idp_logout_url.return_value = idp_logout_url + self._authenticate_with_oauth(client) + response = self.client.get(reverse('logout')) + assert response.status_code == 302 + assert response.url == idp_logout_url + + @mock.patch('django.conf.settings.TPA_AUTOMATIC_LOGOUT_ENABLED', True) + def test_no_automatic_tpa_logout_without_logout_url(self): + """ + Test user is NOT automatically redirected when tpa logout_url is not set + even if TPA_AUTOMATIC_LOGOUT is set to True. + """ + client = self._create_oauth_client() + self._assert_session_logged_out(client) + @ddt.data( ('%22%3E%3Cscript%3Ealert(%27xss%27)%3C/script%3E', 'edx.org'), )