From a80c09a9f2d4a42cd341de3b84b37cdb4e690932 Mon Sep 17 00:00:00 2001 From: Julien Romagnoli Date: Thu, 6 Nov 2014 11:38:00 +0000 Subject: [PATCH] Fix edX name hardcoded in password reset email --- common/djangoapps/student/forms.py | 47 ++++++++++++++++++ .../student/tests/test_microsite.py | 4 +- .../student/tests/test_reset_password.py | 49 +++++++++++++++++++ .../registration/password_reset_email.html | 6 +-- 4 files changed, 101 insertions(+), 5 deletions(-) diff --git a/common/djangoapps/student/forms.py b/common/djangoapps/student/forms.py index 19a3d5fdf4..48c956669b 100644 --- a/common/djangoapps/student/forms.py +++ b/common/djangoapps/student/forms.py @@ -5,6 +5,13 @@ from django import forms from django.contrib.auth.models import User from django.contrib.auth.forms import PasswordResetForm from django.contrib.auth.hashers import UNUSABLE_PASSWORD +from django.contrib.auth.tokens import default_token_generator + +from django.utils.http import int_to_base36 +from django.template import loader + +from django.conf import settings +from microsite_configuration import microsite class PasswordResetFormNoActive(PasswordResetForm): @@ -23,3 +30,43 @@ class PasswordResetFormNoActive(PasswordResetForm): for user in self.users_cache): raise forms.ValidationError(self.error_messages['unusable']) return email + + def save( + self, + domain_override=None, + subject_template_name='registration/password_reset_subject.txt', + email_template_name='registration/password_reset_email.html', + use_https=False, + token_generator=default_token_generator, + from_email=settings.DEFAULT_FROM_EMAIL, + request=None + ): + """ + Generates a one-use only link for resetting password and sends to the + user. + """ + # This import is here because we are copying and modifying the .save from Django 1.4.5's + # django.contrib.auth.forms.PasswordResetForm directly, which has this import in this place. + from django.core.mail import send_mail + for user in self.users_cache: + if not domain_override: + site_name = microsite.get_value( + 'SITE_NAME', + settings.SITE_NAME + ) + else: + site_name = domain_override + context = { + 'email': user.email, + 'site_name': site_name, + 'uid': int_to_base36(user.id), + 'user': user, + 'token': token_generator.make_token(user), + 'protocol': 'https' if use_https else 'http', + 'platform_name': microsite.get_value('platform_name', settings.PLATFORM_NAME) + } + subject = loader.render_to_string(subject_template_name, context) + # Email subject *must not* contain newlines + subject = subject.replace('\n', '') + email = loader.render_to_string(email_template_name, context) + send_mail(subject, email, from_email, [user.email]) diff --git a/common/djangoapps/student/tests/test_microsite.py b/common/djangoapps/student/tests/test_microsite.py index 4564647302..9fe80c9d4e 100644 --- a/common/djangoapps/student/tests/test_microsite.py +++ b/common/djangoapps/student/tests/test_microsite.py @@ -26,7 +26,7 @@ FAKE_MICROSITE = { } -def fake_site_name(name, default=None): # pylint: disable=unused-argument +def fake_site_name(name, default=None): """ create a fake microsite site name """ @@ -36,7 +36,7 @@ def fake_site_name(name, default=None): # pylint: disable=unused-argument return default -def fake_microsite_get_value(name, default=None): # pylint: disable=unused-argument +def fake_microsite_get_value(name, default=None): """ create a fake microsite site name """ diff --git a/common/djangoapps/student/tests/test_reset_password.py b/common/djangoapps/student/tests/test_reset_password.py index 5b538aedb9..e3305a3037 100644 --- a/common/djangoapps/student/tests/test_reset_password.py +++ b/common/djangoapps/student/tests/test_reset_password.py @@ -21,6 +21,8 @@ from student.views import password_reset, password_reset_confirm_wrapper from student.tests.factories import UserFactory from student.tests.test_email import mock_render_to_string +from test_microsite import fake_site_name + @ddt.ddt class ResetPasswordTests(TestCase): @@ -134,6 +136,53 @@ class ResetPasswordTests(TestCase): self.assertIn(expected_msg, msg) + @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', "Test only valid in LMS") + @patch('django.core.mail.send_mail') + @ddt.data(('Crazy Awesome Site', 'Crazy Awesome Site'), (None, 'edX')) + @ddt.unpack + def test_reset_password_email_domain(self, domain_override, platform_name, send_email): + """ + Tests that the right url domain and platform name is included in + the reset password email + """ + with patch("django.conf.settings.PLATFORM_NAME", platform_name): + req = self.request_factory.post( + '/password_reset/', {'email': self.user.email} + ) + req.get_host = Mock(return_value=domain_override) + resp = password_reset(req) + _, msg, _, _ = send_email.call_args[0] + + reset_msg = "you requested a password reset for your user account at {}" + if domain_override: + reset_msg = reset_msg.format(domain_override) + else: + reset_msg = reset_msg.format(settings.SITE_NAME) + + self.assertIn(reset_msg, msg) + + sign_off = "The {} Team".format(platform_name) + self.assertIn(sign_off, msg) + + @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', "Test only valid in LMS") + @patch("microsite_configuration.microsite.get_value", fake_site_name) + @patch('django.core.mail.send_mail') + def test_reset_password_email_microsite(self, send_email): + """ + Tests that the right url domain and platform name is included in + the reset password email + """ + req = self.request_factory.post( + '/password_reset/', {'email': self.user.email} + ) + req.get_host = Mock(return_value=None) + resp = password_reset(req) + _, msg, _, _ = send_email.call_args[0] + + reset_msg = "you requested a password reset for your user account at openedx.localhost" + + self.assertIn(reset_msg, msg) + @patch('student.views.password_reset_confirm') def test_reset_password_bad_token(self, reset_confirm): """Tests bad token and uidb36 in password reset""" diff --git a/lms/templates/registration/password_reset_email.html b/lms/templates/registration/password_reset_email.html index 1952fa1bd6..2bc2a7e222 100644 --- a/lms/templates/registration/password_reset_email.html +++ b/lms/templates/registration/password_reset_email.html @@ -1,15 +1,15 @@ {% load i18n %}{% load url from future %}{% autoescape off %} -{% blocktrans %}You're receiving this e-mail because you requested a password reset for your user account at edx.org.{% endblocktrans %} +{% blocktrans %}You're receiving this e-mail because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %} {% trans "Please go to the following page and choose a new password:" %} {% block reset_link %} -{{ protocol }}://{{domain}}{% url 'student.views.password_reset_confirm_wrapper' uidb36=uid token=token %} +{{ protocol }}://{{ site_name }}{% url 'student.views.password_reset_confirm_wrapper' uidb36=uid token=token %} {% endblock %} {% trans "If you didn't request this change, you can disregard this email - we have not yet reset your password." %} {% trans "Thanks for using our site!" %} -{% blocktrans %}The edX Team{% endblocktrans %} +{% blocktrans %}The {{ platform_name }} Team{% endblocktrans %} {% endautoescape %}