mirror of
				https://github.com/Tivix/django-rest-auth.git
				synced 2025-11-04 01:27:36 +03:00 
			
		
		
		
	password reset and password change refactoring
This commit is contained in:
		
							parent
							
								
									85688940df
								
							
						
					
					
						commit
						a7be2d178b
					
				| 
						 | 
				
			
			@ -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()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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'),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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."})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user