feat: tpa automatic logout (#32193)
* feat: tpa automatic logout * chore: modify toggle documentations * chore: check TPA_AUTOMATIC_LOGOUT_ENABLED in _show_tpa_logout_link * docs: modify method doc
This commit is contained in:
@@ -1216,6 +1216,18 @@ OAUTH_EXPIRE_PUBLIC_CLIENT_DAYS = 30
|
||||
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 <LMS>/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
|
||||
|
||||
@@ -8,6 +8,7 @@ from urllib.parse import parse_qs, urlsplit, urlunsplit # pylint: disable=impor
|
||||
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 @@ class LogoutView(TemplateView):
|
||||
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 <LMS>/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 @@ class LogoutView(TemplateView):
|
||||
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
|
||||
|
||||
@@ -196,6 +196,33 @@ class LogoutTests(TestCase):
|
||||
}
|
||||
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'),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user