diff --git a/common/djangoapps/student/tests/test_activate_account.py b/common/djangoapps/student/tests/test_activate_account.py index ba030a38a1..50ce098c21 100644 --- a/common/djangoapps/student/tests/test_activate_account.py +++ b/common/djangoapps/student/tests/test_activate_account.py @@ -2,6 +2,7 @@ import unittest +import urllib.parse from datetime import datetime from unittest.mock import patch from uuid import uuid4 @@ -17,6 +18,7 @@ from common.djangoapps.student.models import Registration from common.djangoapps.student.tests.factories import UserFactory from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.user_authn.toggles import REDIRECT_TO_AUTHN_MICROFRONTEND +from openedx.features.enterprise_support.tests.factories import EnterpriseCustomerUserFactory FEATURES_WITH_AUTHN_MFE_ENABLED = settings.FEATURES.copy() FEATURES_WITH_AUTHN_MFE_ENABLED['ENABLE_AUTHN_MICROFRONTEND'] = True @@ -167,13 +169,17 @@ class TestActivateAccount(TestCase): @override_settings(LOGIN_REDIRECT_WHITELIST=['localhost:1991']) @override_settings(FEATURES=FEATURES_WITH_AUTHN_MFE_ENABLED) @override_waffle_flag(REDIRECT_TO_AUTHN_MICROFRONTEND, active=True) - def test_account_activation_with_valid_next_url(self): + def test_authenticated_account_activation_with_valid_next_url(self): """ Verify that an activation link with a valid next URL will redirect - the activated user to that next URL, even if the AuthN MFE is active - and redirects to it are enabled. + the activated enterprise user to that next URL, even if the AuthN + MFE is active and redirects to it are enabled. """ self._assert_user_active_state(expected_active_state=False) + EnterpriseCustomerUserFactory(user_id=self.user.id) + + # Make sure the user is authenticated before activation. + self.login() redirect_url = 'http://localhost:1991/pied-piper/learn' base_activation_url = reverse('activate', args=[self.registration.activation_key]) @@ -243,6 +249,38 @@ class TestActivateAccount(TestCase): response = self.client.get(reverse('activate', args=[uuid4().hex])) assert response.url == (login_page_url + 'error') + @override_settings(LOGIN_REDIRECT_WHITELIST=['localhost:1991']) + @override_settings(FEATURES=FEATURES_WITH_AUTHN_MFE_ENABLED) + @override_waffle_flag(REDIRECT_TO_AUTHN_MICROFRONTEND, active=True) + def test_unauthenticated_user_redirects_to_mfe_with_valid_next_url(self): + """ + Verify that if Authn MFE is enabled then authenticated user redirects to + login page with correct account_activation_status param. Additionally, if a valid + `next` redirect URL is provided to the activation URL, it should be included + as a parameter in the login page the requesting user is redirected to. + """ + login_page_url = "{authn_mfe}/login?account_activation_status=".format( + authn_mfe=settings.AUTHN_MICROFRONTEND_URL + ) + + self._assert_user_active_state(expected_active_state=False) + + redirect_url = 'http://localhost:1991/pied-piper/learn' + encoded_next_param = urllib.parse.urlencode({'next': redirect_url}) + base_activation_url = reverse('activate', args=[self.registration.activation_key]) + activation_url = '{base}?{params}'.format( + base=base_activation_url, + params=urlencode({'next': redirect_url}), + ) + + # HTTP_ACCEPT is needed so the safe redirect checks pass. + response = self.client.get(activation_url, HTTP_ACCEPT='*/*') + assert response.url == (login_page_url + 'success&' + encoded_next_param) + + # Access activation link again, the user is redirected to login page with info query param + response = self.client.get(activation_url, HTTP_ACCEPT='*/*') + assert response.url == (login_page_url + 'info&' + encoded_next_param) + def test_authenticated_user_cannot_activate_another_account(self): """ Verify that if a user is authenticated and tries to activate another account, diff --git a/common/djangoapps/student/views/management.py b/common/djangoapps/student/views/management.py index 9ddb4d759f..d0b00233d0 100644 --- a/common/djangoapps/student/views/management.py +++ b/common/djangoapps/student/views/management.py @@ -52,6 +52,7 @@ from openedx.core.djangoapps.theming import helpers as theming_helpers from openedx.core.djangoapps.user_api.preferences import api as preferences_api from openedx.core.djangoapps.user_authn.toggles import should_redirect_to_authn_microfrontend from openedx.core.djangolib.markup import HTML, Text +from openedx.features.enterprise_support.utils import is_enterprise_learner from common.djangoapps.student.email_helpers import generate_activation_email_context from common.djangoapps.student.helpers import DISABLE_UNENROLL_CERT_STATES, cert_info from common.djangoapps.student.message_types import AccountActivation, EmailChange, EmailChangeConfirmation, RecoveryEmailCreate # lint-amnesty, pylint: disable=line-too-long @@ -582,22 +583,33 @@ def activate_account(request, key): extra_tags='account-activation aa-icon', ) - # If a (safe) `next` parameter is provided in the request + # If a safe `next` parameter is provided in the request # and it's not the same as the dashboard, redirect there. # The `get_next_url_for_login_page()` function will only return a safe redirect URL. # If the provided `next` URL is not safe, that function will fill `redirect_to` # with a value of `reverse('dashboard')`. + redirect_url = None if request.GET.get('next'): - redirect_to, root_url = get_next_url_for_login_page(request, include_host=True) - if redirect_to != reverse('dashboard'): - redirect_url = get_redirect_url_with_host(root_url, redirect_to) - return redirect(redirect_url) + redirect_to, root_login_url = get_next_url_for_login_page(request, include_host=True) + + # Don't automatically redirect authenticated users to the redirect_url + # if the `next` value is either: + # 1. "/dashboard" or + # 2. "https://{LMS_ROOT_URL}/dashboard" (which we might provide as a value from the AuthN MFE) + if redirect_to not in ( + root_login_url + reverse('dashboard'), + reverse('dashboard') + ): + redirect_url = get_redirect_url_with_host(root_login_url, redirect_to) if should_redirect_to_authn_microfrontend() and not request.user.is_authenticated: - url_path = f'/login?account_activation_status={activation_message_type}' + params = {'account_activation_status': activation_message_type} + if redirect_url: + params['next'] = redirect_url + url_path = '/login?{}'.format(urllib.parse.urlencode(params)) return redirect(settings.AUTHN_MICROFRONTEND_URL + url_path) - return redirect('dashboard') + return redirect(redirect_url) if redirect_url and is_enterprise_learner(request.user) else redirect('dashboard') @ensure_csrf_cookie