Django: Resetting Passwords (with internal tools)


I have had a task recently. It was about adding a forms/mechanism for resetting a password in our Django based project. We have had our own registration system ongoing... It's a corporate sector project. So you can not go and register yourself. Admins (probably via LDAP sync) will register your email/login in system. So you have to go there and only set yourself a password. For security reasons you can not register. One word.

First I've tried to find standart decision. From reviewed by me were:django-registration and django password-reset. These are nice tools to install and give it a go. But I've needed a more complex decision. And the idea was that own bicycle is always better. So I've thought of django admin and that it has all the things you need to do this yourself in no time. (Actually it's django.contrib.auth part of django, but used out of the box in Admin UI) You can find views you need for this in there. they are:
  • password_reset
  • password_reset_done 
  • password_reset_confirm 
  • password_reset_complete 
To use this method you need:

1. Emails config:

Your typical Django project has settings.py variables to be set for enabling SMTP server. Usually you need this on production to send admins emails about 500 errors and/or system warnings. For our needs python has an internal mail server. I prefer to use it for debug purposes. So good method would be to use it only in Debug case. You can do it by adding those lines to your settings.py:
if DEBUG:
    EMAIL_HOST = 'localhost'
    EMAIL_PORT = 1025
    EMAIL_HOST_USER = ''
    EMAIL_HOST_PASSWORD = ''
    EMAIL_USE_TLS = False
    DEFAULT_FROM_EMAIL = 'testing@example.com'
Here you will have this custom email server set to alternative port. For production you should enter your Sendmail (or whatever you use there) parameters...

Now you will be able to start it running the terminal and executing:
python -m smtpd -n -c DebuggingServer localhost:1025
You can also read abut it here. You will be able to copy/paste email links that should be sent via Django email. Now that you have everything setup for experiments...

2. Config Url's:

urls.py
urlpatterns = patterns('',
    url(r'^user/password/reset/$', 
        'django.contrib.auth.views.password_reset', 
        {'post_reset_redirect' : '/user/password/reset/done/'},
        name="password_reset"),
    (r'^user/password/reset/done/$',
        'django.contrib.auth.views.password_reset_done'),
    (r'^user/password/reset/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$', 
        'django.contrib.auth.views.password_reset_confirm', 
        {'post_reset_redirect' : '/user/password/done/'}),
    (r'^user/password/done/$', 
        'django.contrib.auth.views.password_reset_complete'),
    # ...
)

3. Add Templates:

You have templates for your reset password forms already. Django admin templates/css/js are used there. But I think you would want custom one's for your needs. SO basically all you may need is overriding those templates, specified down here and place them on your templates Django path.
You need to override:
Django templates:

  • registration/password_reset_form.html
  • registration/password_reset_done.html
  • registration/password_reset_confirm.html
  • registration/password_reset_complete.html

Email template:

  • registration/password_reset_email.html 

I'll write down here one's I've used, according to Django's "Batteries included" philosophy.

registration/password_reset_form.html 
{% extends "base.html" %}

{% block title %}Reset Password{% endblock %}

{% block content %}
<p>Please specify your email address to receive instructions for resetting it.</p>

<form action="" method="post">
    <div style="display:none">
        <input type="hidden" value="{{ csrf_token }}" name="csrfmiddlewaretoken">
    </div>
     {{ form.email.errors }}
    <p><label for="id_email">E-mail address:</label> {{ form.email }} <input type="submit" value="Reset password" /></p>
</form>
{% endblock %}

registration/password_reset_done.html 
{% extends "base.html" %}

{% block title %}Password reset successful{% endblock %}

{% block content %}
<p>We've e-mailed you instructions for setting your password to the e-mail address you submitted.</p>
<p>You should be receiving it shortly.</p>
{% endblock %}

registration/password_reset_confirm.html 
{% extends "base.html" %}
{% block title %}Setting New password{% endblock %}

{% block content %}
    {% if validlink %}
        <p>Please enter your new password twice.<br />
           So we can verify you typed it in correctly.</p>
        <form action="" method="post">
            <div style="display:none">
                <input type="hidden" value="{{ csrf_token }}" name="csrfmiddlewaretoken">
            </div>
            <table>
                <tr>
                    <td>{{ form.new_password1.errors }}
                        <label for="id_new_password1">New password:</label></td>
                    <td>{{ form.new_password1 }}</td>
                </tr>
                <tr>
                    <td>{{ form.new_password2.errors }}
                        <label for="id_new_password2">Confirm password:</label></td>
                    <td>{{ form.new_password2 }}</td>
                </tr>
                <tr>
                    <td></td>
                    <td><input type="submit" value="Change my password" /></td>
                </tr>
            </table>
        </form>
    {% else %}
        <h1>Password reset unsuccessful</h1>
        <p>The password reset link was invalid, <br />
        possibly because it has already been used. <br />
        Please request a new password reset.</p>
    {% endif %}
{% endblock %}

registration/password_reset_complete.html 
{% extends "base.html" %}

{% block title %}Password reset complete{% endblock %}

{% block content %}
<p>Your password has been set.  You may go ahead and log in now.</p>
<p><a href="{{ login_url }}">Log in</a></p>
{% endblock %}

registration/password_reset_email.html 
{% autoescape off %}
You're receiving this e-mail because you requested a password reset for your user account at {{ site_name }}.

Please go to the following page and choose a new password:
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url django.contrib.auth.views.password_reset_confirm uidb36=uid, token=token %}
{% endblock %}

Your username, in case you've forgotten: {{ user.username }}

Thanks for using our site!

The {{ site_name }} team.

{% endautoescape %}

SO you're getting password reset system out of the box using only Django included tools. In no time.
Comment me...!

No comments:

Post a Comment