diff --git a/password_reset/views.py b/password_reset/views.py index 88b84a07..db85d587 100644 --- a/password_reset/views.py +++ b/password_reset/views.py @@ -10,6 +10,7 @@ from django.template import loader from django.utils import timezone from django.views import generic +from django.contrib.auth.hashers import get_hasher from .forms import PasswordRecoveryForm, PasswordResetForm from .utils import get_user_model, get_username @@ -20,6 +21,9 @@ class SaltMixin(object): salt = 'password_recovery' url_salt = 'password_recovery_url' + def hash_password(self, psw): + return get_hasher().encode(psw, self.salt) + def loads_with_timestamp(value, salt): """Returns the unsigned value along with its timestamp, the time when it @@ -82,7 +86,14 @@ def send_notification(self): 'site': self.get_site(), 'user': self.user, 'username': get_username(self.user), - 'token': signing.dumps(self.user.pk, salt=self.salt), + 'token': signing.dumps( + { + 'pk': self.user.pk, + 'psw': self.hash_password( + self.user.password + ) + }, + salt=self.salt), 'secure': self.request.is_secure(), } body = loader.render_to_string(self.email_template_name, @@ -121,12 +132,25 @@ def dispatch(self, request, *args, **kwargs): self.kwargs = kwargs try: - pk = signing.loads(kwargs['token'], max_age=self.token_expires, - salt=self.salt) + unsigned_pk_hash = signing.loads(kwargs['token'], + max_age=self.token_expires, + salt=self.salt) except signing.BadSignature: return self.invalid() + try: + pk = unsigned_pk_hash['pk'] + password = unsigned_pk_hash['psw'] + except KeyError: + return self.invalid() + self.user = get_object_or_404(get_user_model(), pk=pk) + + # Ensure the hashed password is same to prevent link to be reused + # TODO: this is assuming the password is changed + if password != self.hash_password(self.user.password): + return self.invalid() + return super(Reset, self).dispatch(request, *args, **kwargs) def invalid(self):