Detect a changed password in Django

4.7k views Asked by At

When a user changes their password, I want to send a signal so that I can do some stuff on some models.

How can I create this signal?

I've looked at the post_save signal for User:

post_save.connect(user_updated, sender=User)

However, there doesn't seem to be anything in there for me to check if the password was changed:

def user_updated(sender, **kwargs):
    print(kwargs) # {'created': False, 'raw': False, 'instance': <User: 100002>, 'update_fields': None, 'signal': <django.db.models.signals.ModelSignal object at 0x7ff8862f03c8>, 'using': 'default'}

I also see that there is a password_change_done auth view, but I'm not sure how I'd use it. https://docs.djangoproject.com/en/1.10/topics/auth/default/#built-in-auth-views

Any ideas?

3

There are 3 answers

3
Messaoud Zahi On BEST ANSWER

You could use a pre_save signal. kwargs['instance'] will contain the updated password and you can get the old password with User.objects.get(id= user.id).password

@receiver(pre_save, sender=User)
def user_updated(sender, **kwargs):
    user = kwargs.get('instance', None)
    if user:
        new_password = user.password
        try:
            old_password = User.objects.get(pk=user.pk).password
        except User.DoesNotExist:
            old_password = None
        if new_password != old_password:
        # do what you need here
0
mendespedro On

I found a solution so you don't have to compare the password hashes, that can be different even if the password is the same.

@receiver(pre_save, sender=get_user_model())
def detect_password_change(sender, instance, **kwargs):
    """
    Checks if the user changed his password
    """
    if instance._password is None:
        return

    try:
        user = get_user_model().objects.get(id=instance.id)
    except get_user_model().DoesNotExist:
        return

    print("password changed")
    # if you get here, the user changed his password
1
Ian Clark On

If you're using a custom user model you could presumably intersect the set_password() call to set a flag on the instance, and then pick up its presence within a signal:

from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.db.models.signals import post_save


class User(AbstractBaseUser, PermissionsMixin):
    def set_password(self, password):
        super(User, self).set_password(password)
        self._set_password = True

    @classmethod
    def user_saved(cls, sender, instance, **kwargs):
        if getattr(instance, '_set_password', False):
            # Take action


post_save.connect(User.user_saved, sender=User)