mirror of
https://github.com/Tivix/django-rest-auth.git
synced 2025-07-22 13:39:45 +03:00
Merge 3a15e58d53
into d25df33a79
This commit is contained in:
commit
18f1a16715
|
@ -8,6 +8,8 @@ Configuration
|
||||||
|
|
||||||
- LOGIN_SERIALIZER - serializer class in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.LoginSerializer``
|
- LOGIN_SERIALIZER - serializer class in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.LoginSerializer``
|
||||||
|
|
||||||
|
- SIMPLE_LOGIN_SERIALIZER - serializer class in ``rest_auth.views.SimpleLoginView``, default value ``rest_auth.serializers.SimpleLoginSerializer``
|
||||||
|
|
||||||
- TOKEN_SERIALIZER - response for successful authentication in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.TokenSerializer``
|
- TOKEN_SERIALIZER - response for successful authentication in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.TokenSerializer``
|
||||||
|
|
||||||
- USER_DETAILS_SERIALIZER - serializer class in ``rest_auth.views.UserDetailsView``, default value ``rest_auth.serializers.UserDetailsSerializer``
|
- USER_DETAILS_SERIALIZER - serializer class in ``rest_auth.views.UserDetailsView``, default value ``rest_auth.serializers.UserDetailsSerializer``
|
||||||
|
@ -34,3 +36,12 @@ Configuration
|
||||||
|
|
||||||
|
|
||||||
- **OLD_PASSWORD_FIELD_ENABLED** - set it to True if you want to have old password verification on password change enpoint (default: False)
|
- **OLD_PASSWORD_FIELD_ENABLED** - set it to True if you want to have old password verification on password change enpoint (default: False)
|
||||||
|
|
||||||
|
|
||||||
|
- **NEW_PASSWORD_2_FIELD_ENABLED** - set it to False if you don't need new password confirmation (default: True)
|
||||||
|
|
||||||
|
|
||||||
|
- **USER_DETAILS_INCLUDED** - is user details urls are needed
|
||||||
|
|
||||||
|
|
||||||
|
- **SIMPLE_LOGIN** - is simplified is used
|
|
@ -4,6 +4,7 @@ from rest_auth.serializers import (
|
||||||
TokenSerializer as DefaultTokenSerializer,
|
TokenSerializer as DefaultTokenSerializer,
|
||||||
UserDetailsSerializer as DefaultUserDetailsSerializer,
|
UserDetailsSerializer as DefaultUserDetailsSerializer,
|
||||||
LoginSerializer as DefaultLoginSerializer,
|
LoginSerializer as DefaultLoginSerializer,
|
||||||
|
SimpleLoginSerializer as DefaultSimpleLoginSerializer,
|
||||||
PasswordResetSerializer as DefaultPasswordResetSerializer,
|
PasswordResetSerializer as DefaultPasswordResetSerializer,
|
||||||
PasswordResetConfirmSerializer as DefaultPasswordResetConfirmSerializer,
|
PasswordResetConfirmSerializer as DefaultPasswordResetConfirmSerializer,
|
||||||
PasswordChangeSerializer as DefaultPasswordChangeSerializer)
|
PasswordChangeSerializer as DefaultPasswordChangeSerializer)
|
||||||
|
@ -23,6 +24,10 @@ LoginSerializer = import_callable(
|
||||||
serializers.get('LOGIN_SERIALIZER', DefaultLoginSerializer)
|
serializers.get('LOGIN_SERIALIZER', DefaultLoginSerializer)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SimpleLoginSerializer = import_callable(
|
||||||
|
serializers.get('SIMPLE_LOGIN_SERIALIZER', DefaultSimpleLoginSerializer)
|
||||||
|
)
|
||||||
|
|
||||||
PasswordResetSerializer = import_callable(
|
PasswordResetSerializer = import_callable(
|
||||||
serializers.get(
|
serializers.get(
|
||||||
'PASSWORD_RESET_SERIALIZER',
|
'PASSWORD_RESET_SERIALIZER',
|
||||||
|
|
|
@ -14,6 +14,34 @@ from rest_framework.authtoken.models import Token
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleLoginSerializer(serializers.Serializer):
|
||||||
|
username = serializers.CharField()
|
||||||
|
password = serializers.CharField(style={'input_type': 'password'})
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
username = attrs.get('username')
|
||||||
|
password = attrs.get('password')
|
||||||
|
|
||||||
|
if username and password:
|
||||||
|
user = authenticate(username=username, password=password)
|
||||||
|
|
||||||
|
else:
|
||||||
|
msg = _('Must include "username" and "password".')
|
||||||
|
raise exceptions.ValidationError(msg)
|
||||||
|
|
||||||
|
# Did we get back an active user?
|
||||||
|
if user:
|
||||||
|
if not user.is_active:
|
||||||
|
msg = _('User account is disabled.')
|
||||||
|
raise exceptions.ValidationError(msg)
|
||||||
|
else:
|
||||||
|
msg = _('Unable to log in with provided credentials.')
|
||||||
|
raise exceptions.ValidationError(msg)
|
||||||
|
|
||||||
|
attrs['user'] = user
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class LoginSerializer(serializers.Serializer):
|
class LoginSerializer(serializers.Serializer):
|
||||||
username = serializers.CharField(required=False, allow_blank=True)
|
username = serializers.CharField(required=False, allow_blank=True)
|
||||||
email = serializers.EmailField(required=False, allow_blank=True)
|
email = serializers.EmailField(required=False, allow_blank=True)
|
||||||
|
@ -172,9 +200,9 @@ class PasswordResetConfirmSerializer(serializers.Serializer):
|
||||||
|
|
||||||
class PasswordChangeSerializer(serializers.Serializer):
|
class PasswordChangeSerializer(serializers.Serializer):
|
||||||
|
|
||||||
old_password = serializers.CharField(max_length=128)
|
old_password = serializers.CharField(max_length=128, required=False)
|
||||||
new_password1 = serializers.CharField(max_length=128)
|
new_password1 = serializers.CharField(max_length=128, required=False)
|
||||||
new_password2 = serializers.CharField(max_length=128)
|
new_password2 = serializers.CharField(max_length=128, required=False)
|
||||||
|
|
||||||
set_password_form_class = SetPasswordForm
|
set_password_form_class = SetPasswordForm
|
||||||
|
|
||||||
|
@ -182,14 +210,30 @@ class PasswordChangeSerializer(serializers.Serializer):
|
||||||
self.old_password_field_enabled = getattr(
|
self.old_password_field_enabled = getattr(
|
||||||
settings, 'OLD_PASSWORD_FIELD_ENABLED', False
|
settings, 'OLD_PASSWORD_FIELD_ENABLED', False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.new_password_2_field_enabled = getattr(
|
||||||
|
settings, 'NEW_PASSWORD_2_FIELD_ENABLED', True
|
||||||
|
)
|
||||||
super(PasswordChangeSerializer, self).__init__(*args, **kwargs)
|
super(PasswordChangeSerializer, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
if not self.old_password_field_enabled:
|
if not self.old_password_field_enabled:
|
||||||
self.fields.pop('old_password')
|
self.fields.pop('old_password')
|
||||||
|
|
||||||
|
if not self.new_password_2_field_enabled:
|
||||||
|
self.fields.pop('new_password2')
|
||||||
|
|
||||||
self.request = self.context.get('request')
|
self.request = self.context.get('request')
|
||||||
self.user = getattr(self.request, 'user', None)
|
self.user = getattr(self.request, 'user', None)
|
||||||
|
|
||||||
|
def get_fields(self):
|
||||||
|
if self.fields:
|
||||||
|
for field in self.fields:
|
||||||
|
self.fields[field].required = True
|
||||||
|
fields = self.fields
|
||||||
|
else:
|
||||||
|
fields = super(PasswordChangeSerializer, self).get_fields()
|
||||||
|
return fields
|
||||||
|
|
||||||
def validate_old_password(self, value):
|
def validate_old_password(self, value):
|
||||||
invalid_password_conditions = (
|
invalid_password_conditions = (
|
||||||
self.old_password_field_enabled,
|
self.old_password_field_enabled,
|
||||||
|
@ -202,6 +246,10 @@ class PasswordChangeSerializer(serializers.Serializer):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
|
|
||||||
|
if not self.new_password_2_field_enabled:
|
||||||
|
attrs['new_password2'] = attrs['new_password1']
|
||||||
|
|
||||||
self.set_password_form = self.set_password_form_class(
|
self.set_password_form = self.set_password_form_class(
|
||||||
user=self.user, data=attrs
|
user=self.user, data=attrs
|
||||||
)
|
)
|
||||||
|
|
|
@ -282,6 +282,44 @@ class APITestCase1(TestCase, BaseAPITestCase):
|
||||||
login_payload['password'] = new_password_payload['new_password1']
|
login_payload['password'] = new_password_payload['new_password1']
|
||||||
self.post(self.login_url, data=login_payload, status_code=200)
|
self.post(self.login_url, data=login_payload, status_code=200)
|
||||||
|
|
||||||
|
@override_settings(OLD_PASSWORD_FIELD_ENABLED=True, NEW_PASSWORD_2_FIELD_ENABLED=False)
|
||||||
|
def test_password_change_without_confirmation(self):
|
||||||
|
login_payload = {
|
||||||
|
"username": self.USERNAME,
|
||||||
|
"password": self.PASS
|
||||||
|
}
|
||||||
|
get_user_model().objects.create_user(self.USERNAME, '', self.PASS)
|
||||||
|
self.post(self.login_url, data=login_payload, status_code=200)
|
||||||
|
self.token = self.response.json['key']
|
||||||
|
|
||||||
|
new_password_payload = {
|
||||||
|
"old_password": "%s!" % self.PASS, # wrong password
|
||||||
|
"new_password1": "new_person",
|
||||||
|
}
|
||||||
|
self.post(
|
||||||
|
self.password_change_url,
|
||||||
|
data=new_password_payload,
|
||||||
|
status_code=400
|
||||||
|
)
|
||||||
|
|
||||||
|
new_password_payload = {
|
||||||
|
"old_password": self.PASS,
|
||||||
|
"new_password1": "new_person",
|
||||||
|
}
|
||||||
|
|
||||||
|
self.post(
|
||||||
|
self.password_change_url,
|
||||||
|
data=new_password_payload,
|
||||||
|
status_code=200
|
||||||
|
)
|
||||||
|
|
||||||
|
# user should not be able to login using old password
|
||||||
|
self.post(self.login_url, data=login_payload, status_code=400)
|
||||||
|
|
||||||
|
# new password should work
|
||||||
|
login_payload['password'] = new_password_payload['new_password1']
|
||||||
|
self.post(self.login_url, data=login_payload, status_code=200)
|
||||||
|
|
||||||
def test_password_reset(self):
|
def test_password_reset(self):
|
||||||
user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
|
user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import patterns, url
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from rest_auth.views import (
|
from rest_auth.views import (
|
||||||
LoginView, LogoutView, UserDetailsView, PasswordChangeView,
|
LoginView, SimpleLoginView, LogoutView, UserDetailsView, PasswordChangeView,
|
||||||
PasswordResetView, PasswordResetConfirmView
|
PasswordResetView, PasswordResetConfirmView
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,10 +14,25 @@ urlpatterns = patterns(
|
||||||
name='rest_password_reset'),
|
name='rest_password_reset'),
|
||||||
url(r'^password/reset/confirm/$', PasswordResetConfirmView.as_view(),
|
url(r'^password/reset/confirm/$', PasswordResetConfirmView.as_view(),
|
||||||
name='rest_password_reset_confirm'),
|
name='rest_password_reset_confirm'),
|
||||||
url(r'^login/$', LoginView.as_view(), name='rest_login'),
|
|
||||||
# URLs that require a user to be logged in with a valid session / token.
|
# URLs that require a user to be logged in with a valid session / token.
|
||||||
url(r'^logout/$', LogoutView.as_view(), name='rest_logout'),
|
url(r'^logout/$', LogoutView.as_view(), name='rest_logout'),
|
||||||
url(r'^user/$', UserDetailsView.as_view(), name='rest_user_details'),
|
|
||||||
url(r'^password/change/$', PasswordChangeView.as_view(),
|
url(r'^password/change/$', PasswordChangeView.as_view(),
|
||||||
name='rest_password_change'),
|
name='rest_password_change'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if getattr(settings, 'USER_DETAILS_INCLUDED', True):
|
||||||
|
urlpatterns += patterns(
|
||||||
|
'',
|
||||||
|
url(r'^user/$', UserDetailsView.as_view(), name='rest_user_details'),
|
||||||
|
)
|
||||||
|
|
||||||
|
if getattr(settings, 'SIMPLE_LOGIN', False):
|
||||||
|
urlpatterns += patterns(
|
||||||
|
'',
|
||||||
|
url(r'^login/$', SimpleLoginView.as_view(), name='rest_login'),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
urlpatterns += patterns(
|
||||||
|
'',
|
||||||
|
url(r'^login/$', LoginView.as_view(), name='rest_login'),
|
||||||
|
)
|
||||||
|
|
|
@ -10,11 +10,41 @@ from rest_framework.authtoken.models import Token
|
||||||
from rest_framework.generics import RetrieveUpdateAPIView
|
from rest_framework.generics import RetrieveUpdateAPIView
|
||||||
|
|
||||||
from .app_settings import (
|
from .app_settings import (
|
||||||
TokenSerializer, UserDetailsSerializer, LoginSerializer,
|
TokenSerializer, UserDetailsSerializer, SimpleLoginSerializer,
|
||||||
PasswordResetSerializer, PasswordResetConfirmSerializer,
|
LoginSerializer, PasswordResetSerializer, PasswordResetConfirmSerializer,
|
||||||
PasswordChangeSerializer
|
PasswordChangeSerializer
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class SimpleLoginView(GenericAPIView):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Check the credentials and authenticated if the credentials are valid .
|
||||||
|
Calls Django Auth login method to register User ID
|
||||||
|
in Django session framework
|
||||||
|
|
||||||
|
Accept the following POST parameters: username, password
|
||||||
|
"""
|
||||||
|
permission_classes = (AllowAny,)
|
||||||
|
serializer_class = SimpleLoginSerializer
|
||||||
|
|
||||||
|
def login(self):
|
||||||
|
self.user = self.serializer.validated_data['user']
|
||||||
|
|
||||||
|
if getattr(settings, 'REST_SESSION_LOGIN', True):
|
||||||
|
login(self.request, self.user)
|
||||||
|
|
||||||
|
def get_error_response(self):
|
||||||
|
return Response(
|
||||||
|
self.serializer.errors, status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
self.serializer = self.get_serializer(data=self.request.data)
|
||||||
|
if not self.serializer.is_valid():
|
||||||
|
return self.get_error_response()
|
||||||
|
self.login()
|
||||||
|
return Response({}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
class LoginView(GenericAPIView):
|
class LoginView(GenericAPIView):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user