views: better display password reset instructions (#38054)

This commit is contained in:
Valentin Deniaud 2020-01-07 15:11:57 +01:00
parent 8e4831c64e
commit abed3fa8cc
4 changed files with 68 additions and 17 deletions

View File

@ -0,0 +1,37 @@
{% extends "authentic2/base-page.html" %}
{% load i18n gadjo %}
{% block page-title %}
{% trans "Password reset instructions" %}
{% endblock %}
{% block content %}
<p><strong>
{% blocktrans with email=request.session.reset_email %}
If your email address exists in our database, an email has been sent to {{ email }}.
{% endblocktrans %}
</strong></p>
<p><strong>
{% blocktrans %}
Follow the instructions in this email in order to choose a new password.
{% endblocktrans %}
</strong></p>
{% block advice %}
<p>
{% blocktrans %}
The email may take several minutes to be received. It can also be
considered as spam: please look in your "junk mail" folder.
{% endblocktrans %}
</p>
<p>
{% blocktrans %}
If you still have not received the instructions, add "{{from_email_address}}"
to your address book or authorized sender list, and then repeat the
password reset process.
{% endblocktrans %}
{% endblock %}
</p>
{% block back %}
<p><a href="{% url 'auth_login' %}">{% trans "Back to login" %}</a></p>
{% endblock %}
{% endblock %}

View File

@ -81,6 +81,9 @@ accounts_urlpatterns = [
url(r'^password/reset/$',
views.password_reset,
name='password_reset'),
url(r'^password/reset/instructions/$',
views.password_reset_instructions,
name='password_reset_instructions'),
# Legacy, only there to provide old view names to resolver
url(r'^password/change/$',

View File

@ -626,11 +626,13 @@ def csrf_failure_view(request, reason=""):
return HttpResponseRedirect(request.get_full_path())
class PasswordResetView(cbv.NextURLViewMixin, FormView):
class PasswordResetView(FormView):
'''Ask for an email and send a password reset link by mail'''
form_class = passwords_forms.PasswordResetForm
title = _('Password Reset')
next_url_default = '/'
def get_success_url(self):
return reverse('password_reset_instructions')
def get_template_names(self):
return [
@ -653,16 +655,24 @@ class PasswordResetView(cbv.NextURLViewMixin, FormView):
def form_valid(self, form):
form.save()
# return to next URL
messages.info(self.request, _('If your email address exists in our '
'database, you will receive an email '
'containing instructions to reset '
'your password'))
self.request.session['reset_email'] = form.cleaned_data['email']
return super(PasswordResetView, self).form_valid(form)
password_reset = PasswordResetView.as_view()
class PasswordResetInstructionsView(TemplateView):
template_name = 'registration/password_reset_instructions.html'
def get_context_data(self, **kwargs):
ctx = super(PasswordResetInstructionsView, self).get_context_data(**kwargs)
ctx['from_email_address'] = parseaddr(settings.DEFAULT_FROM_EMAIL)[1]
return ctx
password_reset_instructions = PasswordResetInstructionsView.as_view()
class PasswordResetConfirmView(cbv.RedirectToNextURLViewMixin, FormView):
'''Validate password reset link, show a set password form and login
the user.

View File

@ -39,13 +39,18 @@ def test_send_password_reset_email(app, simple_user, mailoutbox):
assert str(app.session['_auth_user_id']) == str(simple_user.pk)
def test_view(app, simple_user, mailoutbox):
url = reverse('password_reset') + '?next=/moncul/'
def test_view(app, simple_user, mailoutbox, settings):
url = reverse('password_reset')
resp = app.get(url, status=200)
resp.form.set('email', simple_user.email)
assert len(mailoutbox) == 0
settings.DEFAULT_FROM_EMAIL = 'show only addr <noreply@example.net>'
resp = resp.form.submit()
assert resp['Location'].endswith('/moncul/')
assert resp['Location'].endswith('/instructions/')
resp = resp.follow()
assert simple_user.email in resp.text
assert '"noreply@example.net"' in resp.text
assert 'show only addr' not in resp.text
assert len(mailoutbox) == 1
url = utils.get_link_from_mail(mailoutbox[0])
relative_url = url.split('testserver')[1]
@ -55,32 +60,28 @@ def test_view(app, simple_user, mailoutbox):
resp = resp.form.submit()
# verify user is logged
assert str(app.session['_auth_user_id']) == str(simple_user.pk)
# verify next_url was kept
assert resp['Location'].endswith('/moncul/')
with override_settings(A2_USER_CAN_RESET_PASSWORD=False):
url = reverse('password_reset') + '?next=/moncul/'
url = reverse('password_reset')
app.get(url, status=404)
def test_user_filter(app, simple_user, mailoutbox, settings):
settings.A2_USER_FILTER = {'username': 'xxx'} # will not match simple_user
url = reverse('password_reset') + '?next=/moncul/'
url = reverse('password_reset')
resp = app.get(url, status=200)
resp.form.set('email', simple_user.email)
assert len(mailoutbox) == 0
resp = resp.form.submit()
assert resp['Location'].endswith('/moncul/')
assert len(mailoutbox) == 0
def test_user_exclude(app, simple_user, mailoutbox, settings):
settings.A2_USER_EXCLUDE = {'username': simple_user.username} # will not match simple_user
url = reverse('password_reset') + '?next=/moncul/'
url = reverse('password_reset')
resp = app.get(url, status=200)
resp.form.set('email', simple_user.email)
assert len(mailoutbox) == 0
resp = resp.form.submit()
assert resp['Location'].endswith('/moncul/')
assert len(mailoutbox) == 0