Update sign in email address for continued access
This commit is contained in:
@@ -110,6 +110,12 @@ def login_and_registration_form(request, initial_mode="login"):
|
||||
} for message in messages.get_messages(request) if 'account-activation' in message.tags
|
||||
]
|
||||
|
||||
account_recovery_messages = [
|
||||
{
|
||||
'message': message.message, 'tags': message.tags
|
||||
} for message in messages.get_messages(request) if 'account-recovery' in message.tags
|
||||
]
|
||||
|
||||
# Otherwise, render the combined login/registration page
|
||||
context = {
|
||||
'data': {
|
||||
@@ -123,6 +129,7 @@ def login_and_registration_form(request, initial_mode="login"):
|
||||
'PASSWORD_RESET_SUPPORT_LINK', settings.PASSWORD_RESET_SUPPORT_LINK
|
||||
) or settings.SUPPORT_SITE_LINK,
|
||||
'account_activation_messages': account_activation_messages,
|
||||
'account_recovery_messages': account_recovery_messages,
|
||||
|
||||
# Include form descriptions retrieved from the user API.
|
||||
# We could have the JS client make these requests directly,
|
||||
|
||||
@@ -17,6 +17,7 @@ from django.contrib.messages.middleware import MessageMiddleware
|
||||
from django.contrib.sessions.middleware import SessionMiddleware
|
||||
from django.core import mail
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.http import Http404
|
||||
from django.urls import reverse
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
@@ -28,6 +29,8 @@ from oauth2_provider.models import RefreshToken as dot_refresh_token
|
||||
from provider.oauth2.models import AccessToken as dop_access_token
|
||||
from provider.oauth2.models import RefreshToken as dop_refresh_token
|
||||
from testfixtures import LogCapture
|
||||
from waffle.models import Switch
|
||||
from waffle.testutils import override_switch
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from openedx.core.djangoapps.user_authn.views.login_form import login_and_registration_form
|
||||
@@ -36,9 +39,11 @@ from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
|
||||
from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme_context
|
||||
from openedx.core.djangoapps.user_api.accounts.api import activate_account, create_account
|
||||
from openedx.core.djangoapps.user_api.errors import UserAPIInternalError
|
||||
from openedx.core.djangoapps.user_api.accounts.utils import ENABLE_SECONDARY_EMAIL_FEATURE_SWITCH
|
||||
from openedx.core.djangolib.js_utils import dump_js_escaped_json
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
|
||||
from student.tests.factories import AccountRecoveryFactory
|
||||
from third_party_auth.tests.testutil import ThirdPartyAuthTestMixin, simulate_running_pipeline
|
||||
from util.testing import UrlResetMixin
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
@@ -76,6 +81,12 @@ class UserAccountUpdateTest(CacheIsolationTestCase, UrlResetMixin):
|
||||
activation_key = create_account(self.USERNAME, self.OLD_PASSWORD, self.OLD_EMAIL)
|
||||
activate_account(activation_key)
|
||||
|
||||
self.account_recovery = AccountRecoveryFactory.create(user=User.objects.get(email=self.OLD_EMAIL))
|
||||
self.enable_account_recovery_switch = Switch.objects.create(
|
||||
name=ENABLE_SECONDARY_EMAIL_FEATURE_SWITCH,
|
||||
active=True
|
||||
)
|
||||
|
||||
# Login
|
||||
result = self.client.login(username=self.USERNAME, password=self.OLD_PASSWORD)
|
||||
self.assertTrue(result)
|
||||
@@ -250,6 +261,63 @@ class UserAccountUpdateTest(CacheIsolationTestCase, UrlResetMixin):
|
||||
response = getattr(self.client, method)(url)
|
||||
self.assertEqual(response.status_code, 405)
|
||||
|
||||
@override_switch(ENABLE_SECONDARY_EMAIL_FEATURE_SWITCH, active=False)
|
||||
def test_404_if_account_recovery_not_enabled(self):
|
||||
with mock.patch('openedx.core.djangoapps.user_api.accounts.api.request_account_recovery',
|
||||
side_effect=UserAPIInternalError):
|
||||
self._recover_account()
|
||||
self.assertRaises(Http404)
|
||||
|
||||
def test_account_recovery_failure(self):
|
||||
with mock.patch('openedx.core.djangoapps.user_api.accounts.api.request_account_recovery',
|
||||
side_effect=UserAPIInternalError):
|
||||
self._recover_account()
|
||||
self.assertRaises(UserAPIInternalError)
|
||||
|
||||
@override_settings(FEATURES=FEATURES_WITH_FAILED_PASSWORD_RESET_EMAIL)
|
||||
def test_account_recovery_failure_email(self):
|
||||
"""Test that a password reset failure email notification is sent, when enabled."""
|
||||
# Log the user out
|
||||
self.client.logout()
|
||||
|
||||
with LogCapture(LOGGER_NAME, level=logging.INFO) as logger:
|
||||
bad_email = 'doesnotexist@example.com'
|
||||
response = self._recover_account(email=bad_email)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
logger.check(
|
||||
(
|
||||
LOGGER_NAME,
|
||||
"WARNING", "Account recovery attempt via invalid secondary email '{email}'.".format(
|
||||
email=bad_email
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def test_password_change_rate_limited_during_account_recovery(self):
|
||||
# Log out the user created during test setup, to prevent the view from
|
||||
# selecting the logged-in user's email address over the email provided
|
||||
# in the POST data
|
||||
self.client.logout()
|
||||
|
||||
# Make many consecutive bad requests in an attempt to trigger the rate limiter
|
||||
for __ in xrange(self.INVALID_ATTEMPTS):
|
||||
self._recover_account(email=self.NEW_EMAIL)
|
||||
|
||||
response = self._recover_account(email=self.NEW_EMAIL)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
@ddt.data(
|
||||
('post', 'account_recovery', []),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_require_http_method_during_account_recovery(self, correct_method, url_name, args):
|
||||
wrong_methods = {'get', 'put', 'post', 'head', 'options', 'delete'} - {correct_method}
|
||||
url = reverse(url_name, args=args)
|
||||
|
||||
for method in wrong_methods:
|
||||
response = getattr(self.client, method)(url)
|
||||
self.assertEqual(response.status_code, 405)
|
||||
|
||||
def _change_password(self, email=None):
|
||||
"""Request to change the user's password. """
|
||||
data = {}
|
||||
@@ -259,6 +327,15 @@ class UserAccountUpdateTest(CacheIsolationTestCase, UrlResetMixin):
|
||||
|
||||
return self.client.post(path=reverse('password_change_request'), data=data)
|
||||
|
||||
def _recover_account(self, email=None):
|
||||
"""Request to create the user's password. """
|
||||
data = {}
|
||||
|
||||
if email:
|
||||
data['email'] = email
|
||||
|
||||
return self.client.post(path=reverse('account_recovery'), data=data)
|
||||
|
||||
def _create_dop_tokens(self, user=None):
|
||||
"""Create dop access token for given user if user provided else for default user."""
|
||||
if not user:
|
||||
|
||||
Reference in New Issue
Block a user