Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get notified when a user changes password or requests a password reset?

Tags:

django

For a password change I am using auth_views.password_change and for the password reset auth_views.password_reset.

How can I be notified when a user successfully changes their password? I do not need to know old nor new password. Just that the event took place, and for which user.

Similarly, I would like to get notified when someone requested a password reset and also when they successfully completed the reset procedure.

Can i do the above with signals or some simple patching? Or do I need to write my own views to do this?

like image 868
Krystian Cybulski Avatar asked Feb 03 '12 17:02

Krystian Cybulski


3 Answers

Create a decorator:

def notify_admins(func):
    def wrapper(request, *args, **kwargs):
        # send email to admins
        return func(request, *args, **kwargs)
    return wrapper

Then, just add wrap it around the appropriate views in your urls.py:

urlpatterns = patterns('',
    ...
    (r'^password_change/done/$', notify_admins(auth_views.password_change_done)),
    (r'^password_reset/done/$', notify_admins(auth_views.password_reset_done)),
    (r'^reset/done/$', notify_admins(auth_views.password_reset_complete)),
    ...
)

Keep in mind that sending email directly from a view, or in this case a decorator, will tie up the request. Instead of sending the email there directly, it would be better to create a custom signal and a handler that will fire off a thread to actually send the email. Then, in the decorator, you simply send the signal.

like image 174
Chris Pratt Avatar answered Nov 21 '22 04:11

Chris Pratt


You could write a custom password_change_form that you pass to password_change. This form would extend django's PasswordChangeForm overriding its save method to first notify you of the change and then call it's parent PasswordChangeForms save method.

Docs on password_change view: https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.views.password_change

Docs on ChangeForm: https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.forms.PasswordChangeForm

Code for PasswordChangeForm: https://code.djangoproject.com/browser/django/trunk/django/contrib/auth/forms.py

like image 30
Eric Conner Avatar answered Nov 21 '22 04:11

Eric Conner


Beginning in Django 1.9, you can define your define your own password validators. You could even simply re-define an existing one, if you like. When you do, add a method:

from django.contrib.auth.password_validation import MinimumLengthValidator
class MyPasswordValidator(MinimumLengthValidator):

    def password_changed(self, password, user):
        # put your password changed logic here

Be sure to include your new class in your settings as follows:

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'my_package.password_validators.MyPasswordValidator',
        'OPTIONS': {
            'min_length': 8,
        }
    },
    ...
]

Now, each time a password is changed by the user, your class MyPasswordValidator will be notified. In my experience, this is the best way to do this because:

  1. When using signals to capture these events, you will also capture events where the system re-encoded an existing password due to a change in hashing parameters, in most cases, you would not want to capture these events and there's no obvious way to prevent it with signals.
  2. You could simple add a function-call in the save() method of all your password-handling forms, but this becomes difficult when you want to do the same with the built in admin change password form and will not help you if password changes are made programmatically outside of a form.

I will caution you to be aware that the password parameter in password_changed() is in fact the user's raw password. Take care when handling this and absolutely never store this anywhere unencrypted/unhashed.

like image 41
mkoistinen Avatar answered Nov 21 '22 02:11

mkoistinen