password reset and password change refactoring

This commit is contained in:
Mateusz Sikora 2014-10-07 15:08:08 +02:00
parent 85688940df
commit a7be2d178b
4 changed files with 120 additions and 120 deletions

View File

@ -1,5 +1,12 @@
from django.contrib.auth import get_user_model
from django.conf import settings
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
try:
from django.utils.http import urlsafe_base64_decode as uid_decoder
except:
# make compatible with django 1.5
from django.utils.http import base36_to_int as uid_decoder
from django.contrib.auth.tokens import default_token_generator
from rest_framework import serializers
from rest_framework.authtoken.models import Token
@ -43,20 +50,6 @@ class UserDetailsSerializer(serializers.ModelSerializer):
read_only_fields = ('id', 'last_login', 'is_active', 'date_joined')
class SetPasswordSerializer(serializers.Serializer):
"""
Serializer for changing Django User password.
"""
new_password1 = serializers.CharField(max_length=128)
new_password2 = serializers.CharField(max_length=128)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
return super(SetPasswordSerializer, self).__init__(*args, **kwargs)
class PasswordResetSerializer(serializers.Serializer):
"""
@ -64,3 +57,86 @@ class PasswordResetSerializer(serializers.Serializer):
"""
email = serializers.EmailField()
password_reset_form_class = PasswordResetForm
def validate_email(self, attrs, source):
# Create PasswordResetForm with the serializer
self.reset_form = self.password_reset_form_class(data=attrs)
if not self.reset_form.is_valid():
raise serializers.ValidationError('Error')
return attrs
def save(self):
request = self.context.get('request')
# Set some values to trigger the send_email method.
opts = {
'use_https': request.is_secure(),
'from_email': getattr(settings, 'DEFAULT_FROM_EMAIL'),
'request': request,
}
self.reset_form.save(**opts)
class PasswordResetConfirmSerializer(serializers.Serializer):
"""
Serializer for requesting a password reset e-mail.
"""
new_password1 = serializers.CharField(max_length=128)
new_password2 = serializers.CharField(max_length=128)
uid = serializers.CharField(required=True)
token = serializers.CharField(required=True)
set_password_form_class = SetPasswordForm
def custom_validation(self, attrs):
pass
def validate(self, attrs):
self._errors = {}
# Get the UserModel
UserModel = get_user_model()
# Decode the uidb64 to uid to get User object
try:
uid = uid_decoder(attrs['uid'])
self.user = UserModel._default_manager.get(pk=uid)
except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
self._errors['uid'] = ['Invalid value']
self.custom_validation(attrs)
# Construct SetPasswordForm instance
self.set_password_form = self.set_password_form_class(user=self.user,
data=attrs)
if not self.set_password_form.is_valid():
self._errors['token'] = ['Invalid value']
if not default_token_generator.check_token(self.user, attrs['token']):
self._errors['token'] = ['Invalid value']
def save(self):
self.set_password_form.save()
class PasswordChangeSerializer(serializers.Serializer):
new_password1 = serializers.CharField(max_length=128)
new_password2 = serializers.CharField(max_length=128)
set_password_form_class = SetPasswordForm
def validate(self, attrs):
request = self.context.get('request')
self.set_password_form = self.set_password_form_class(user=request.user,
data=attrs)
if not self.set_password_form.is_valid():
self._errors = self.set_password_form.errors
return None
return attrs
def save(self):
self.set_password_form.save()

View File

@ -253,16 +253,17 @@ class APITestCase1(TestCase, BaseAPITestCase):
# call password reset
mail_count = len(mail.outbox)
payload = {'email': self.EMAIL}
self.post(self.password_reset_url, data=payload)
self.post(self.password_reset_url, data=payload, status_code=200)
self.assertEqual(len(mail.outbox), mail_count + 1)
url_kwargs = self._generate_uid_and_token(user)
data = {
'new_password1': self.NEW_PASS,
'new_password2': self.NEW_PASS
'new_password2': self.NEW_PASS,
'uid': url_kwargs['uid'],
'token': url_kwargs['token']
}
url = reverse('rest_password_reset_confirm', kwargs=url_kwargs)
url = reverse('rest_password_reset_confirm')
self.post(url, data=data, status_code=200)
payload = {

View File

@ -10,7 +10,7 @@ urlpatterns = patterns('rest_auth.views',
# URLs that do not require a session or valid token
url(r'^password/reset/$', PasswordReset.as_view(),
name='rest_password_reset'),
url(r'^password/reset/confirm/(?P<uid>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
url(r'^password/reset/confirm/$',
PasswordResetConfirm.as_view(
), name='rest_password_reset_confirm'),
url(r'^login/$', Login.as_view(), name='rest_login'),

View File

@ -1,11 +1,4 @@
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
from django.contrib.auth import login, logout, get_user_model
from django.contrib.auth.tokens import default_token_generator
try:
from django.utils.http import urlsafe_base64_decode as uid_decoder
except:
# make compatible with django 1.5
from django.utils.http import base36_to_int as uid_decoder
from django.contrib.auth import login, logout
from django.conf import settings
from rest_framework import status
@ -19,7 +12,8 @@ from rest_framework.authtoken.models import Token
from rest_framework.generics import RetrieveUpdateAPIView
from rest_auth.serializers import (TokenSerializer, UserDetailsSerializer,
LoginSerializer, SetPasswordSerializer, PasswordResetSerializer)
LoginSerializer, PasswordResetSerializer, PasswordResetConfirmSerializer,
PasswordChangeSerializer)
class LoggedInRESTAPIView(APIView):
@ -122,38 +116,18 @@ class PasswordReset(LoggedOutRESTAPIView, GenericAPIView):
"""
serializer_class = PasswordResetSerializer
password_reset_form_class = PasswordResetForm
def post(self, request):
def post(self, request, *args, **kwargs):
# Create a serializer with request.DATA
serializer = self.serializer_class(data=request.DATA)
serializer = self.get_serializer(data=request.DATA)
if serializer.is_valid():
# Create PasswordResetForm with the serializer
reset_form = self.password_reset_form_class(data=serializer.data)
if reset_form.is_valid():
# Sett some values to trigger the send_email method.
opts = {
'use_https': request.is_secure(),
'from_email': getattr(settings, 'DEFAULT_FROM_EMAIL'),
'request': request,
}
reset_form.save(**opts)
# Return the success message with OK HTTP status
return Response(
{"success": "Password reset e-mail has been sent."},
status=status.HTTP_200_OK)
else:
return Response(reset_form._errors,
status=status.HTTP_400_BAD_REQUEST)
else:
if not serializer.is_valid():
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
serializer.save()
# Return the success message with OK HTTP status
return Response({"success": "Password reset e-mail has been sent."},
status=status.HTTP_200_OK)
class PasswordResetConfirm(LoggedOutRESTAPIView, GenericAPIView):
@ -166,49 +140,15 @@ class PasswordResetConfirm(LoggedOutRESTAPIView, GenericAPIView):
Returns the success/fail message.
"""
serializer_class = SetPasswordSerializer
serializer_class = PasswordResetConfirmSerializer
def post(self, request, uid=None, token=None):
# Get the UserModel
UserModel = get_user_model()
# Decode the uidb64 to uid to get User object
try:
uid = uid_decoder(uid)
user = UserModel._default_manager.get(pk=uid)
except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
user = None
# If we get the User object
if user:
serializer = self.serializer_class(data=request.DATA, user=user)
if serializer.is_valid():
# Construct SetPasswordForm instance
form = SetPasswordForm(user=user, data=serializer.data)
if form.is_valid():
if default_token_generator.check_token(user, token):
form.save()
# Return the success message with OK HTTP status
return Response(
{"success":
"Password has been reset with the new password."},
status=status.HTTP_200_OK)
else:
return Response(
{"error": "Invalid password reset token."},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response(form._errors, status=status.HTTP_400_BAD_REQUEST)
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
else:
return Response({"errors": "Couldn\'t find the user from uid."}, status=status.HTTP_400_BAD_REQUEST)
def post(self, request):
serializer = self.get_serializer(data=request.DATA)
if not serializer.is_valid():
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
serializer.save()
return Response({"success": "Password has been reset with the new password."})
class PasswordChange(LoggedInRESTAPIView, GenericAPIView):
@ -220,29 +160,12 @@ class PasswordChange(LoggedInRESTAPIView, GenericAPIView):
Returns the success/fail message.
"""
serializer_class = SetPasswordSerializer
serializer_class = PasswordChangeSerializer
def post(self, request):
# Create a serializer with request.DATA
serializer = self.serializer_class(data=request.DATA)
if serializer.is_valid():
# Construct the SetPasswordForm instance
form = SetPasswordForm(user=request.user, data=serializer.data)
if form.is_valid():
form.save()
# Return the success message with OK HTTP status
return Response({"success": "New password has been saved."},
status=status.HTTP_200_OK)
else:
return Response(form._errors,
status=status.HTTP_400_BAD_REQUEST)
else:
serializer = self.get_serializer(data=request.DATA)
if not serializer.is_valid():
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
status=status.HTTP_400_BAD_REQUEST)
serializer.save()
return Response({"success": "New password has been saved."})