This commit is contained in:
Eugena Mihailikova 2015-09-29 12:49:34 +00:00
commit 18f1a16715
6 changed files with 157 additions and 8 deletions

View File

@ -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

View File

@ -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',

View File

@ -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
) )

View File

@ -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)

View File

@ -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'),
)

View File

@ -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):