mirror of
https://github.com/Tivix/django-rest-auth.git
synced 2025-07-15 10:22:18 +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.contrib.auth import get_user_model
|
||||||
from django.conf import settings
|
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 import serializers
|
||||||
from rest_framework.authtoken.models import Token
|
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')
|
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):
|
class PasswordResetSerializer(serializers.Serializer):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -64,3 +57,86 @@ class PasswordResetSerializer(serializers.Serializer):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
email = serializers.EmailField()
|
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
|
# call password reset
|
||||||
mail_count = len(mail.outbox)
|
mail_count = len(mail.outbox)
|
||||||
payload = {'email': self.EMAIL}
|
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)
|
self.assertEqual(len(mail.outbox), mail_count + 1)
|
||||||
|
|
||||||
url_kwargs = self._generate_uid_and_token(user)
|
url_kwargs = self._generate_uid_and_token(user)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'new_password1': self.NEW_PASS,
|
'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)
|
self.post(url, data=data, status_code=200)
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
|
|
|
@ -10,7 +10,7 @@ urlpatterns = patterns('rest_auth.views',
|
||||||
# URLs that do not require a session or valid token
|
# URLs that do not require a session or valid token
|
||||||
url(r'^password/reset/$', PasswordReset.as_view(),
|
url(r'^password/reset/$', PasswordReset.as_view(),
|
||||||
name='rest_password_reset'),
|
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(
|
PasswordResetConfirm.as_view(
|
||||||
), name='rest_password_reset_confirm'),
|
), name='rest_password_reset_confirm'),
|
||||||
url(r'^login/$', Login.as_view(), name='rest_login'),
|
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
|
||||||
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.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
@ -19,7 +12,8 @@ from rest_framework.authtoken.models import Token
|
||||||
from rest_framework.generics import RetrieveUpdateAPIView
|
from rest_framework.generics import RetrieveUpdateAPIView
|
||||||
|
|
||||||
from rest_auth.serializers import (TokenSerializer, UserDetailsSerializer,
|
from rest_auth.serializers import (TokenSerializer, UserDetailsSerializer,
|
||||||
LoginSerializer, SetPasswordSerializer, PasswordResetSerializer)
|
LoginSerializer, PasswordResetSerializer, PasswordResetConfirmSerializer,
|
||||||
|
PasswordChangeSerializer)
|
||||||
|
|
||||||
|
|
||||||
class LoggedInRESTAPIView(APIView):
|
class LoggedInRESTAPIView(APIView):
|
||||||
|
@ -122,38 +116,18 @@ class PasswordReset(LoggedOutRESTAPIView, GenericAPIView):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
serializer_class = PasswordResetSerializer
|
serializer_class = PasswordResetSerializer
|
||||||
password_reset_form_class = PasswordResetForm
|
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request, *args, **kwargs):
|
||||||
# Create a serializer with request.DATA
|
# Create a serializer with request.DATA
|
||||||
serializer = self.serializer_class(data=request.DATA)
|
serializer = self.get_serializer(data=request.DATA)
|
||||||
|
|
||||||
if serializer.is_valid():
|
if not 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:
|
|
||||||
return Response(serializer.errors,
|
return Response(serializer.errors,
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
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):
|
class PasswordResetConfirm(LoggedOutRESTAPIView, GenericAPIView):
|
||||||
|
@ -166,49 +140,15 @@ class PasswordResetConfirm(LoggedOutRESTAPIView, GenericAPIView):
|
||||||
Returns the success/fail message.
|
Returns the success/fail message.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
serializer_class = SetPasswordSerializer
|
serializer_class = PasswordResetConfirmSerializer
|
||||||
|
|
||||||
def post(self, request, uid=None, token=None):
|
def post(self, request):
|
||||||
# Get the UserModel
|
serializer = self.get_serializer(data=request.DATA)
|
||||||
UserModel = get_user_model()
|
if not serializer.is_valid():
|
||||||
|
return Response(serializer.errors,
|
||||||
# Decode the uidb64 to uid to get User object
|
status=status.HTTP_400_BAD_REQUEST)
|
||||||
try:
|
serializer.save()
|
||||||
uid = uid_decoder(uid)
|
return Response({"success": "Password has been reset with the new password."})
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordChange(LoggedInRESTAPIView, GenericAPIView):
|
class PasswordChange(LoggedInRESTAPIView, GenericAPIView):
|
||||||
|
@ -220,29 +160,12 @@ class PasswordChange(LoggedInRESTAPIView, GenericAPIView):
|
||||||
Returns the success/fail message.
|
Returns the success/fail message.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
serializer_class = SetPasswordSerializer
|
serializer_class = PasswordChangeSerializer
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
# Create a serializer with request.DATA
|
serializer = self.get_serializer(data=request.DATA)
|
||||||
serializer = self.serializer_class(data=request.DATA)
|
if not serializer.is_valid():
|
||||||
|
|
||||||
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:
|
|
||||||
return Response(serializer.errors,
|
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