Merge pull request #15618 from open-craft/bdero/failed-password-reset-email

Add optional password reset failure emails
This commit is contained in:
Michael Frey
2018-01-24 10:06:16 -05:00
committed by GitHub
5 changed files with 62 additions and 2 deletions

View File

@@ -283,6 +283,10 @@ FEATURES = {
# Whether or not the dynamic EnrollmentTrackUserPartition should be registered.
'ENABLE_ENROLLMENT_TRACK_USER_PARTITION': True,
# Whether to send an email for failed password reset attempts or not. This is mainly useful for notifying users
# that they don't have an account associated with email addresses they believe they've registered with.
'ENABLE_PASSWORD_RESET_FAILURE_EMAIL': False,
# Whether archived courses (courses with end dates in the past) should be
# shown in Studio in a separate list.
'ENABLE_SEPARATE_ARCHIVED_COURSES': True,

View File

@@ -58,6 +58,9 @@ from openedx.core.djangoapps.user_api.errors import UserAPIInternalError
LOGGER_NAME = 'audit'
User = get_user_model() # pylint:disable=invalid-name
FEATURES_WITH_FAILED_PASSWORD_RESET_EMAIL = settings.FEATURES.copy()
FEATURES_WITH_FAILED_PASSWORD_RESET_EMAIL['ENABLE_PASSWORD_RESET_FAILURE_EMAIL'] = True
@ddt.ddt
class StudentAccountUpdateTest(CacheIsolationTestCase, UrlResetMixin):
@@ -147,6 +150,28 @@ class StudentAccountUpdateTest(CacheIsolationTestCase, UrlResetMixin):
self._change_password()
self.assertRaises(UserAPIInternalError)
@override_settings(FEATURES=FEATURES_WITH_FAILED_PASSWORD_RESET_EMAIL)
def test_password_reset_failure_email(self):
"""Test that a password reset failure email notification is sent, when enabled."""
# Log the user out
self.client.logout()
bad_email = 'doesnotexist@example.com'
response = self._change_password(email=bad_email)
self.assertEqual(response.status_code, 200)
# Check that an email was sent
self.assertEqual(len(mail.outbox), 1)
# Verify that the body contains the failed password reset message
email_body = mail.outbox[0].body
self.assertIn(
'However, there is currently no user account associated with your email address: {email}'.format(
email=bad_email
),
email_body,
)
@ddt.data(True, False)
def test_password_change_logged_out(self, send_email):
# Log the user out

View File

@@ -9,9 +9,11 @@ from django.conf import settings
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden
from django.core.mail import send_mail
from django.core.urlresolvers import resolve, reverse
from django.http import HttpRequest, HttpResponse, HttpResponseBadRequest, HttpResponseForbidden
from django.shortcuts import redirect
from django.template import loader
from django.utils.translation import ugettext as _
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.decorators.http import require_http_methods
@@ -217,6 +219,25 @@ def password_change_request_handler(request):
AUDIT_LOG.info("Invalid password reset attempt")
# Increment the rate limit counter
limiter.tick_bad_request_counter(request)
# If enabled, send an email saying that a password reset was attempted, but that there is
# no user associated with the email
if configuration_helpers.get_value('ENABLE_PASSWORD_RESET_FAILURE_EMAIL',
settings.FEATURES['ENABLE_PASSWORD_RESET_FAILURE_EMAIL']):
context = {
'failed': True,
'email_address': email,
'platform_name': configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME),
}
subject = loader.render_to_string('emails/password_reset_subject.txt', context)
subject = ''.join(subject.splitlines())
message = loader.render_to_string('registration/password_reset_email.html', context)
from_email = configuration_helpers.get_value('email_from_address', settings.DEFAULT_FROM_EMAIL)
try:
send_mail(subject, message, from_email, [email])
except Exception: # pylint: disable=broad-except
log.exception(u'Unable to send password reset failure email notification from "%s"', from_email)
except UserAPIInternalError as err:
log.exception('Error occured during password change for user {email}: {error}'
.format(email=email, error=err))

View File

@@ -421,6 +421,10 @@ FEATURES = {
# Whether HTML XBlocks/XModules return HTML content with the Course Blocks API student_view_data
'ENABLE_HTML_XBLOCK_STUDENT_VIEW_DATA': False,
# Whether to send an email for failed password reset attempts or not. This is mainly useful for notifying users
# that they don't have an account associated with email addresses they believe they've registered with.
'ENABLE_PASSWORD_RESET_FAILURE_EMAIL': False,
}
# Settings for the course reviews tool template and identification key, set either to None to disable course reviews

View File

@@ -1,6 +1,11 @@
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this e-mail because you requested a password reset for your user account at {{ platform_name }}.{% endblocktrans %}
{% if failed %}
{% blocktrans %}However, there is currently no user account associated with your email address: {{ email_address }}.{% endblocktrans %}
{% trans "If you did not request this change, you can ignore this email." %}
{% else %}
{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ protocol }}://{{ site_name }}{% url 'password_reset_confirm' uidb36=uid token=token %}
@@ -9,6 +14,7 @@
{% 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!" %}
{% endif %}
{% blocktrans %}The {{ platform_name }} Team{% endblocktrans %}