diff --git a/common/test/acceptance/tests/lms/test_lms.py b/common/test/acceptance/tests/lms/test_lms.py index 81d72b3ba2..cfd0ab0368 100644 --- a/common/test/acceptance/tests/lms/test_lms.py +++ b/common/test/acceptance/tests/lms/test_lms.py @@ -148,7 +148,7 @@ class LoginFromCombinedPageTest(UniqueCourseTest): # Expect that we're shown a failure message self.assertIn( - "No active user with the provided email address exists.", + "No user with the provided email address exists.", self.login_page.wait_for_errors() ) diff --git a/lms/djangoapps/student_account/test/test_views.py b/lms/djangoapps/student_account/test/test_views.py index 47bb961eba..e715e2ceef 100644 --- a/lms/djangoapps/student_account/test/test_views.py +++ b/lms/djangoapps/student_account/test/test_views.py @@ -289,7 +289,11 @@ class StudentAccountUpdateTest(UrlResetMixin, TestCase): # Send the view the email address tied to the inactive user response = self._change_password(email=self.NEW_EMAIL) - self.assertEqual(response.status_code, 400) + + # Expect that the activation email is still sent, + # since the user may have lost the original activation email. + self.assertEqual(response.status_code, 200) + self.assertEqual(len(mail.outbox), 1) def test_password_change_no_user(self): # Log out the user created during test setup diff --git a/lms/djangoapps/student_account/views.py b/lms/djangoapps/student_account/views.py index 28eedcfd7f..9460f5fe01 100644 --- a/lms/djangoapps/student_account/views.py +++ b/lms/djangoapps/student_account/views.py @@ -9,6 +9,7 @@ from django.http import ( from django.shortcuts import redirect from django.core.urlresolvers import reverse from django.core.mail import send_mail +from django.utils.translation import ugettext as _ from django_future.csrf import ensure_csrf_cookie from django.contrib.auth.decorators import login_required from django.views.decorators.http import require_http_methods @@ -261,11 +262,11 @@ def password_change_request_handler(request): # Increment the rate limit counter limiter.tick_bad_request_counter(request) - return HttpResponseBadRequest("No active user with the provided email address exists.") + return HttpResponseBadRequest(_("No user with the provided email address exists.")) return HttpResponse(status=200) else: - return HttpResponseBadRequest("No email address provided.") + return HttpResponseBadRequest(_("No email address provided.")) def _third_party_auth_context(request): diff --git a/openedx/core/djangoapps/user_api/api/account.py b/openedx/core/djangoapps/user_api/api/account.py index e79969f1b7..92a7e9a4fc 100644 --- a/openedx/core/djangoapps/user_api/api/account.py +++ b/openedx/core/djangoapps/user_api/api/account.py @@ -9,8 +9,8 @@ information and preferences). from django.conf import settings from django.db import transaction, IntegrityError from django.core.validators import validate_email, validate_slug, ValidationError -from django.contrib.auth.forms import PasswordResetForm +from ..forms import PasswordResetFormNoActive from ..models import User, UserProfile, Registration, PendingEmailChange from ..helpers import intercept_errors @@ -353,9 +353,9 @@ def request_password_change(email, orig_host, is_secure): """ # Binding data to a form requires that the data be passed as a dictionary # to the Form class constructor. - form = PasswordResetForm({'email': email}) + form = PasswordResetFormNoActive({'email': email}) - # Validate that an active user exists with the given email address. + # Validate that a user exists with the given email address. if form.is_valid(): # Generate a single-use link for performing a password reset # and email it to the user. @@ -365,7 +365,7 @@ def request_password_change(email, orig_host, is_secure): use_https=is_secure ) else: - # No active user with the provided email address exists. + # No user with the provided email address exists. raise AccountUserNotFound diff --git a/openedx/core/djangoapps/user_api/forms.py b/openedx/core/djangoapps/user_api/forms.py new file mode 100644 index 0000000000..8dd6130623 --- /dev/null +++ b/openedx/core/djangoapps/user_api/forms.py @@ -0,0 +1,2 @@ +# TODO: eventually move this implementation into the user_api +from student.forms import PasswordResetFormNoActive diff --git a/openedx/core/djangoapps/user_api/tests/test_account_api.py b/openedx/core/djangoapps/user_api/tests/test_account_api.py index 27b708abce..506487e56d 100644 --- a/openedx/core/djangoapps/user_api/tests/test_account_api.py +++ b/openedx/core/djangoapps/user_api/tests/test_account_api.py @@ -292,18 +292,24 @@ class AccountApiTest(TestCase): result = re.search('(?Phttps?://[^\s]+)', email_body) self.assertIsNot(result, None) - @raises(account_api.AccountUserNotFound) - @ddt.data(True, False) - def test_request_password_change_invalid_user(self, create_inactive_account): - if create_inactive_account: - # Create an account, but do not activate it - account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL) - - account_api.request_password_change(self.EMAIL, self.ORIG_HOST, self.IS_SECURE) + @skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in LMS') + def test_request_password_change_invalid_user(self): + with self.assertRaises(account_api.AccountUserNotFound): + account_api.request_password_change(self.EMAIL, self.ORIG_HOST, self.IS_SECURE) # Verify that no email messages have been sent self.assertEqual(len(mail.outbox), 0) + @skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in LMS') + def test_request_password_change_inactive_user(self): + # Create an account, but do not activate it + account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL) + + account_api.request_password_change(self.EMAIL, self.ORIG_HOST, self.IS_SECURE) + + # Verify that the activation email was still sent + self.assertEqual(len(mail.outbox), 1) + def _assert_is_datetime(self, timestamp): if not timestamp: return False