diff --git a/common/djangoapps/student/tests/test_views.py b/common/djangoapps/student/tests/test_views.py index 3b62712cbf..49d6dd1bf3 100644 --- a/common/djangoapps/student/tests/test_views.py +++ b/common/djangoapps/student/tests/test_views.py @@ -157,22 +157,36 @@ class LogoutTests(TestCase): self.client.post(reverse('oauth2:capture'), data, follow=True) self.assertListEqual(self.client.session[AUTHORIZED_CLIENTS_SESSION_KEY], [oauth_client.client_id]) - def assert_logout_redirects(self): + def assert_logout_redirects_to_root(self): """ Verify logging out redirects the user to the homepage. """ response = self.client.get(reverse('logout')) self.assertRedirects(response, '/', fetch_redirect_response=False) - def test_switch(self): + def assert_logout_redirects_with_target(self): + """ Verify logging out with a redirect_url query param redirects the user to the target. """ + url = '{}?{}'.format(reverse('logout'), 'redirect_url=/courses') + response = self.client.get(url) + self.assertRedirects(response, '/courses', fetch_redirect_response=False) + + def test_switch_default(self): """ Verify the IDA logout functionality is disabled if the associated switch is disabled. """ LogoutViewConfiguration.objects.create(enabled=False) oauth_client = self.create_oauth_client() self.authenticate_with_oauth(oauth_client) - self.assert_logout_redirects() + self.assert_logout_redirects_to_root() + + def test_switch_with_redirect_url(self): + """ Verify the IDA logout functionality is disabled if the associated switch is disabled. """ + LogoutViewConfiguration.objects.create(enabled=False) + oauth_client = self.create_oauth_client() + self.authenticate_with_oauth(oauth_client) + self.assert_logout_redirects_with_target() def test_without_session_value(self): """ Verify logout works even if the session does not contain an entry with the authenticated OpenID Connect clients.""" - self.assert_logout_redirects() + self.assert_logout_redirects_to_root() + self.assert_logout_redirects_with_target() def test_client_logout(self): """ Verify the context includes a list of the logout URIs of the authenticated OpenID Connect clients. diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index f3ad473453..ccd6503bc6 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -31,7 +31,7 @@ from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbid from django.shortcuts import redirect from django.utils.encoding import force_bytes, force_text from django.utils.translation import ungettext -from django.utils.http import base36_to_int, urlsafe_base64_encode, urlencode +from django.utils.http import base36_to_int, is_safe_url, urlsafe_base64_encode, urlencode from django.utils.translation import ugettext as _, get_language from django.views.decorators.csrf import csrf_exempt, ensure_csrf_cookie from django.views.decorators.http import require_POST, require_GET @@ -2672,7 +2672,21 @@ class LogoutView(TemplateView): template_name = 'logout.html' # Keep track of the page to which the user should ultimately be redirected. - target = reverse_lazy('cas-logout') if settings.FEATURES.get('AUTH_USE_CAS') else '/' + default_target = reverse_lazy('cas-logout') if settings.FEATURES.get('AUTH_USE_CAS') else '/' + + @property + def target(self): + """ + If a redirect_url is specified in the querystring for this request, and the value is a url + with the same host, the view will redirect to this page after rendering the template. + If it is not specified, we will use the default target url. + """ + target_url = self.request.GET.get('redirect_url') + + if target_url and is_safe_url(target_url, self.request.META.get('HTTP_HOST')): + return target_url + else: + return self.default_target def dispatch(self, request, *args, **kwargs): # pylint: disable=missing-docstring # We do not log here, because we have a handler registered to perform logging on successful logouts.