diff --git a/demo/demo/urls.py b/demo/demo/urls.py
index c83bb8f..54d06ad 100644
--- a/demo/demo/urls.py
+++ b/demo/demo/urls.py
@@ -1,8 +1,8 @@
-from django.conf.urls import patterns, include, url
+from django.conf.urls import include, url
from django.contrib import admin
from django.views.generic import TemplateView, RedirectView
-urlpatterns = patterns('',
+urlpatterns = [
url(r'^$', TemplateView.as_view(template_name="home.html"), name='home'),
url(r'^signup/$', TemplateView.as_view(template_name="signup.html"),
name='signup'),
@@ -36,4 +36,4 @@ urlpatterns = patterns('',
url(r'^account/', include('allauth.urls')),
url(r'^admin/', include(admin.site.urls)),
url(r'^accounts/profile/$', RedirectView.as_view(url='/', permanent=True), name='profile-redirect'),
-)
+]
diff --git a/docs/api_endpoints.rst b/docs/api_endpoints.rst
index 48e475e..1f9660f 100644
--- a/docs/api_endpoints.rst
+++ b/docs/api_endpoints.rst
@@ -7,6 +7,7 @@ Basic
- /rest-auth/login/ (POST)
- username (string)
+ - email (string)
- password (string)
@@ -55,16 +56,6 @@ Registration
- password2
- email
- .. note:: This endpoint is based on ``allauth.account.views.SignupView`` and uses the same form as in this view. To override fields you have to create custom Signup Form and define it in django settings:
-
- .. code-block:: python
-
- ACCOUNT_FORMS = {
- 'signup': 'path.to.custom.SignupForm'
- }
-
- See allauth documentation for more details.
-
- /rest-auth/registration/verify-email/ (POST)
- key
diff --git a/docs/configuration.rst b/docs/configuration.rst
index 040c1cf..3746234 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -29,12 +29,19 @@ Configuration
...
}
-- **REST_SESSION_LOGIN** - Enable session login in Login API view (default: True)
+- **REST_AUTH_REGISTRATION_SERIALIZERS**
+ You can define your custom serializers for registration endpoint.
+ Possible key values:
+
+ - REGISTER_SERIALIZER - serializer class in ``rest_auth.register.views.RegisterView``, default value ``rest_auth.register.serializers.RegisterSerializer``
+
+- **REST_AUTH_TOKEN_MODEL** - model class for tokens, default value ``rest_framework.authtoken.models``
+
+- **REST_AUTH_TOKEN_CREATOR** - callable to create tokens, default value ``rest_auth.utils.default_create_token``.
+
+- **REST_SESSION_LOGIN** - Enable session login in Login API view (default: True)
- **OLD_PASSWORD_FIELD_ENABLED** - set it to True if you want to have old password verification on password change enpoint (default: False)
- **LOGOUT_ON_PASSWORD_CHANGE** - set to False if you want to keep the current user logged in after a password change
-
-- **REST_USE_JWT** - If enabled, this will use `django-rest-framework-jwt ` as a backend, and instead of session based tokens or Social Login keys, it will return a JWT.
-
diff --git a/docs/faq.rst b/docs/faq.rst
index 5faeee6..ff04c13 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -17,7 +17,12 @@ FAQ
djang-allauth https://github.com/pennersr/django-allauth/blob/master/allauth/account/views.py#L190
-2. How can I update UserProfile assigned to User model?
+2. I get an error: Reverse for 'password_reset_confirm' not found.
+
+ You need to add `password_reset_confirm` url into your ``urls.py`` (at the top of any other included urls). Please check the ``urls.py`` module inside demo app example for more details.
+
+
+3. How can I update UserProfile assigned to User model?
Assuming you already have UserProfile model defined like this
diff --git a/docs/installation.rst b/docs/installation.rst
index 38eaadf..92251dc 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -38,7 +38,7 @@ You're good to go now!
Registration (optional)
-----------------------
-1. If you want to enable standard registration process you will need to install ``django-allauth`` - see this doc for installation http://django-allauth.readthedocs.org/en/latest/installation.html.
+1. If you want to enable standard registration process you will need to install ``django-allauth`` by using ``pip install django-rest-auth[extras]`` or ``pip install django-rest-auth[with_social]``.
2. Add ``allauth``, ``allauth.account`` and ``rest_auth.registration`` apps to INSTALLED_APPS in your django settings.py:
diff --git a/rest_auth/app_settings.py b/rest_auth/app_settings.py
index b52064f..1b75fe6 100644
--- a/rest_auth/app_settings.py
+++ b/rest_auth/app_settings.py
@@ -8,8 +8,10 @@ from rest_auth.serializers import (
PasswordResetSerializer as DefaultPasswordResetSerializer,
PasswordResetConfirmSerializer as DefaultPasswordResetConfirmSerializer,
PasswordChangeSerializer as DefaultPasswordChangeSerializer)
-from .utils import import_callable
+from .utils import import_callable, default_create_token
+create_token = import_callable(
+ getattr(settings, 'REST_AUTH_TOKEN_CREATOR', default_create_token))
serializers = getattr(settings, 'REST_AUTH_SERIALIZERS', {})
diff --git a/rest_auth/models.py b/rest_auth/models.py
index e703865..a132f9c 100644
--- a/rest_auth/models.py
+++ b/rest_auth/models.py
@@ -1,3 +1,10 @@
-# from django.db import models
+from django.conf import settings
+
+from rest_framework.authtoken.models import Token as DefaultTokenModel
+
+from .utils import import_callable
# Register your models here.
+
+TokenModel = import_callable(
+ getattr(settings, 'REST_AUTH_TOKEN_MODEL', DefaultTokenModel))
diff --git a/rest_auth/registration/app_settings.py b/rest_auth/registration/app_settings.py
new file mode 100644
index 0000000..227b45b
--- /dev/null
+++ b/rest_auth/registration/app_settings.py
@@ -0,0 +1,11 @@
+from django.conf import settings
+
+from rest_auth.registration.serializers import (
+ RegisterSerializer as DefaultRegisterSerializer)
+from ..utils import import_callable
+
+
+serializers = getattr(settings, 'REST_AUTH_REGISTER_SERIALIZERS', {})
+
+RegisterSerializer = import_callable(
+ serializers.get('REGISTER_SERIALIZER', DefaultRegisterSerializer))
diff --git a/rest_auth/registration/serializers.py b/rest_auth/registration/serializers.py
index 5f5efd6..e3c83fb 100644
--- a/rest_auth/registration/serializers.py
+++ b/rest_auth/registration/serializers.py
@@ -1,17 +1,26 @@
from django.http import HttpRequest
from django.conf import settings
+try:
+ from allauth.account import app_settings as allauth_settings
+ from allauth.utils import (email_address_exists,
+ get_username_max_length)
+ from allauth.account.adapter import get_adapter
+ from allauth.account.utils import setup_user_email
+except ImportError:
+ raise ImportError('allauth needs to be added to INSTALLED_APPS.')
+
from rest_framework import serializers
from requests.exceptions import HTTPError
# Import is needed only if we are using social login, in which
# case the allauth.socialaccount will be declared
-try:
- from allauth.socialaccount.helpers import complete_social_login
-except ImportError:
- raise ImportError('allauth.socialaccount needs to be installed.')
-if 'allauth.socialaccount' not in settings.INSTALLED_APPS:
- raise ImportError('allauth.socialaccount needs to be added to INSTALLED_APPS.')
+if 'allauth.socialaccount' in settings.INSTALLED_APPS:
+ try:
+ from allauth.socialaccount.helpers import complete_social_login
+ except ImportError:
+ pass
+
class SocialLoginSerializer(serializers.Serializer):
@@ -109,3 +118,57 @@ class SocialLoginSerializer(serializers.Serializer):
attrs['user'] = login.account.user
return attrs
+
+
+class RegisterSerializer(serializers.Serializer):
+ username = serializers.CharField(
+ max_length=get_username_max_length(),
+ min_length=allauth_settings.USERNAME_MIN_LENGTH,
+ required=allauth_settings.USERNAME_REQUIRED
+ )
+ email = serializers.EmailField(required=allauth_settings.EMAIL_REQUIRED)
+ password1 = serializers.CharField(required=True, write_only=True)
+ password2 = serializers.CharField(required=True, write_only=True)
+
+ def validate_username(self, username):
+ username = get_adapter().clean_username(username)
+ return username
+
+ def validate_email(self, email):
+ email = get_adapter().clean_email(email)
+ if allauth_settings.UNIQUE_EMAIL:
+ if email and email_address_exists(email):
+ raise serializers.ValidationError(
+ "A user is already registered with this e-mail address.")
+ return email
+
+ def validate_password1(self, password):
+ return get_adapter().clean_password(password)
+
+ def validate(self, data):
+ if data['password1'] != data['password2']:
+ raise serializers.ValidationError("The two password fields didn't match.")
+ return data
+
+ def custom_signup(self, request, user):
+ pass
+
+ def get_cleaned_data(self):
+ return {
+ 'username': self.validated_data.get('username', ''),
+ 'password1': self.validated_data.get('password1', ''),
+ 'email': self.validated_data.get('email', '')
+ }
+
+ def save(self, request):
+ adapter = get_adapter()
+ user = adapter.new_user(request)
+ self.cleaned_data = self.get_cleaned_data()
+ adapter.save_user(request, user, self)
+ self.custom_signup(request, user)
+ setup_user_email(request, user, [])
+ return user
+
+
+class VerifyEmailSerializer(serializers.Serializer):
+ key = serializers.CharField()
diff --git a/rest_auth/registration/urls.py b/rest_auth/registration/urls.py
index abdd8b5..9e56c3b 100644
--- a/rest_auth/registration/urls.py
+++ b/rest_auth/registration/urls.py
@@ -1,10 +1,9 @@
from django.views.generic import TemplateView
-from django.conf.urls import patterns, url
+from django.conf.urls import url
from .views import RegisterView, VerifyEmailView
-urlpatterns = patterns(
- '',
+urlpatterns = [
url(r'^$', RegisterView.as_view(), name='rest_register'),
url(r'^verify-email/$', VerifyEmailView.as_view(), name='rest_verify_email'),
@@ -21,4 +20,4 @@ urlpatterns = patterns(
# djang-allauth https://github.com/pennersr/django-allauth/blob/master/allauth/account/views.py#L190
url(r'^account-confirm-email/(?P\w+)/$', TemplateView.as_view(),
name='account_confirm_email'),
-)
+]
diff --git a/rest_auth/registration/views.py b/rest_auth/registration/views.py
index ce68a86..dc96a50 100644
--- a/rest_auth/registration/views.py
+++ b/rest_auth/registration/views.py
@@ -1,101 +1,53 @@
-from django.http import HttpRequest
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
+from rest_framework.generics import CreateAPIView
from rest_framework import status
-from rest_framework.authtoken.models import Token
+from rest_framework.exceptions import MethodNotAllowed
-from allauth.account.views import SignupView, ConfirmEmailView
+from allauth.account.views import ConfirmEmailView
from allauth.account.utils import complete_signup
-from allauth.account import app_settings
+from allauth.account import app_settings as allauth_settings
-from rest_auth.app_settings import TokenSerializer
-from rest_auth.registration.serializers import SocialLoginSerializer
+from rest_auth.app_settings import (TokenSerializer,
+ create_token)
+from rest_auth.registration.serializers import (SocialLoginSerializer,
+ VerifyEmailSerializer)
from rest_auth.views import LoginView
+from rest_auth.models import TokenModel
+from .app_settings import RegisterSerializer
from rest_auth.utils import jwt_encode
+class RegisterView(CreateAPIView):
+ serializer_class = RegisterSerializer
+ permission_classes = (AllowAny, )
+ token_model = TokenModel
-class RegisterView(APIView, SignupView):
- """
- Accepts the credentials and creates a new user
- if user does not exist already
- Return the REST Token if the credentials are valid and authenticated.
- Calls allauth complete_signup method
+ def get_response_data(self, user):
+ if allauth_settings.EMAIL_VERIFICATION == \
+ allauth_settings.EmailVerificationMethod.MANDATORY:
+ return {}
- Accept the following POST parameters: username, email, password
- Return the REST Framework Token Object's key.
- """
+ return TokenSerializer(user.auth_token).data
- permission_classes = (AllowAny,)
- allowed_methods = ('POST', 'OPTIONS', 'HEAD')
- token_model = Token
-
- def get_serializer_class(self):
- if getattr(settings, 'REST_USE_JWT', False):
- return JWTSerializer
- else:
- return TokenSerializer
+ def create(self, request, *args, **kwargs):
+ serializer = self.get_serializer(data=request.data)
+ serializer.is_valid(raise_exception=True)
+ user = self.perform_create(serializer)
+ headers = self.get_success_headers(serializer.data)
- def get(self, *args, **kwargs):
- return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
+ return Response(self.get_response_data(user), status=status.HTTP_201_CREATED, headers=headers)
- def put(self, *args, **kwargs):
- return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
-
- def form_valid(self, form):
- self.user = form.save(self.request)
-
- if getattr(settings, 'REST_USE_JWT', False):
- self.token = jwt_encode(self.user)
-
- else:
- self.token, created = self.token_model.objects.get_or_create(
- user=self.user
- )
- if isinstance(self.request, HttpRequest):
- request = self.request
- else:
- request = self.request._request
- return complete_signup(request, self.user,
- app_settings.EMAIL_VERIFICATION,
- self.get_success_url())
-
- def get_form_kwargs(self, *args, **kwargs):
- kwargs = super(RegisterView, self).get_form_kwargs(*args, **kwargs)
- kwargs['data'] = self.request.data
- return kwargs
-
- def post(self, request, *args, **kwargs):
- self.initial = {}
- form_class = self.get_form_class()
- self.form = self.get_form(form_class)
- if self.form.is_valid():
- self.form_valid(self.form)
- return self.get_response()
- else:
- return self.get_response_with_errors()
-
- def get_response(self):
- serializer_class = self.get_serializer_class()
-
- if getattr(settings, 'REST_USE_JWT', False):
- data = {
- 'user': self.user,
- 'token': self.token
- }
- serializer = serializer_class(instance=data,
- context={'request': self.request})
- else:
- serializer = serializer_class(instance=self.token,
- context={'request': self.request})
-
- return Response(serializer.data, status=status.HTTP_201_CREATED)
-
- def get_response_with_errors(self):
- return Response(self.form.errors, status=status.HTTP_400_BAD_REQUEST)
+ def perform_create(self, serializer):
+ user = serializer.save(self.request)
+ create_token(self.token_model, user, serializer)
+ complete_signup(self.request._request, user,
+ allauth_settings.EMAIL_VERIFICATION,
+ None)
+ return user
class VerifyEmailView(APIView, ConfirmEmailView):
@@ -104,10 +56,12 @@ class VerifyEmailView(APIView, ConfirmEmailView):
allowed_methods = ('POST', 'OPTIONS', 'HEAD')
def get(self, *args, **kwargs):
- return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
+ raise MethodNotAllowed('GET')
def post(self, request, *args, **kwargs):
- self.kwargs['key'] = self.request.data.get('key', '')
+ serializer = VerifyEmailSerializer(data=request.data)
+ serializer.is_valid(raise_exception=True)
+ self.kwargs['key'] = serializer.validated_data['key']
confirmation = self.get_object()
confirmation.confirm(self.request)
return Response({'message': 'ok'}, status=status.HTTP_200_OK)
@@ -137,4 +91,4 @@ class SocialLoginView(LoginView):
-------------
"""
- serializer_class = SocialLoginSerializer
+ serializer_class = SocialLoginSerializer
\ No newline at end of file
diff --git a/rest_auth/serializers.py b/rest_auth/serializers.py
index 1145826..f1e8173 100644
--- a/rest_auth/serializers.py
+++ b/rest_auth/serializers.py
@@ -6,8 +6,9 @@ from django.utils.http import urlsafe_base64_decode as uid_decoder
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import force_text
+from .models import TokenModel
+
from rest_framework import serializers, exceptions
-from rest_framework.authtoken.models import Token
from rest_framework.exceptions import ValidationError
# Get the UserModel
@@ -19,43 +20,73 @@ class LoginSerializer(serializers.Serializer):
email = serializers.EmailField(required=False, allow_blank=True)
password = serializers.CharField(style={'input_type': 'password'})
+ def _validate_email(self, email, password):
+ user = None
+
+ if email and password:
+ user = authenticate(email=email, password=password)
+ else:
+ msg = _('Must include "email" and "password".')
+ raise exceptions.ValidationError(msg)
+
+ return user
+
+ def _validate_username(self, username, password):
+ user = None
+
+ if username and password:
+ user = authenticate(username=username, password=password)
+ else:
+ msg = _('Must include "username" and "password".')
+ raise exceptions.ValidationError(msg)
+
+ return user
+
+ def _validate_username_email(self, username, email, password):
+ user = None
+
+ if email and password:
+ user = authenticate(email=email, password=password)
+ elif username and password:
+ user = authenticate(username=username, password=password)
+ else:
+ msg = _('Must include either "username" or "email" and "password".')
+ raise exceptions.ValidationError(msg)
+
+ return user
+
def validate(self, attrs):
username = attrs.get('username')
email = attrs.get('email')
password = attrs.get('password')
+ user = None
+
if 'allauth' in settings.INSTALLED_APPS:
from allauth.account import app_settings
+
# Authentication through email
if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.EMAIL:
- if email and password:
- user = authenticate(email=email, password=password)
- else:
- msg = _('Must include "email" and "password".')
- raise exceptions.ValidationError(msg)
+ user = self._validate_email(email, password)
+
# Authentication through username
- elif app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.USERNAME:
- if username and password:
- user = authenticate(username=username, password=password)
- else:
- msg = _('Must include "username" and "password".')
- raise exceptions.ValidationError(msg)
+ if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.USERNAME:
+ user = self._validate_username(username, password)
+
# Authentication through either username or email
else:
- if email and password:
- user = authenticate(email=email, password=password)
- elif username and password:
- user = authenticate(username=username, password=password)
- else:
- msg = _('Must include either "username" or "email" and "password".')
- raise exceptions.ValidationError(msg)
-
- elif username and password:
- user = authenticate(username=username, password=password)
+ user = self._validate_username_email(username, email, password)
else:
- msg = _('Must include "username" and "password".')
- raise exceptions.ValidationError(msg)
+ # Authentication without using allauth
+ if email:
+ try:
+ username = UserModel.objects.get(email__iexact=email).username
+ except UserModel.DoesNotExist:
+ pass
+
+ if username:
+ user = self._validate_username_email(username, '', password)
# Did we get back an active user?
if user:
@@ -84,7 +115,7 @@ class TokenSerializer(serializers.ModelSerializer):
"""
class Meta:
- model = Token
+ model = TokenModel
fields = ('key',)
class UserDetailsSerializer(serializers.ModelSerializer):
@@ -114,15 +145,17 @@ class PasswordResetSerializer(serializers.Serializer):
password_reset_form_class = PasswordResetForm
+ def get_email_options(self):
+ """ Override this method to change default e-mail options
+ """
+ return {}
+
def validate_email(self, value):
# Create PasswordResetForm with the serializer
self.reset_form = self.password_reset_form_class(data=self.initial_data)
if not self.reset_form.is_valid():
raise serializers.ValidationError(_('Error'))
- if not UserModel.objects.filter(email__iexact=value).exists():
- raise serializers.ValidationError(_('Invalid e-mail address'))
-
return value
def save(self):
@@ -133,6 +166,8 @@ class PasswordResetSerializer(serializers.Serializer):
'from_email': getattr(settings, 'DEFAULT_FROM_EMAIL'),
'request': request,
}
+
+ opts.update(self.get_email_options())
self.reset_form.save(**opts)
diff --git a/rest_auth/tests/test_api.py b/rest_auth/tests/test_api.py
index 3f844f9..f6cc839 100644
--- a/rest_auth/tests/test_api.py
+++ b/rest_auth/tests/test_api.py
@@ -2,6 +2,7 @@ from django.core.urlresolvers import reverse
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.core import mail
+from django.conf import settings
from django.test.utils import override_settings
from django.utils.encoding import force_text
@@ -90,18 +91,50 @@ class APITestCase1(TestCase, BaseAPITestCase):
# test empty payload
self.post(self.login_url, data={}, status_code=400)
- @override_settings(REST_USE_JWT=True)
- def test_login_jwt(self):
+ def test_login_by_email(self):
+ # starting test without allauth app
+ settings.INSTALLED_APPS.remove('allauth')
+
payload = {
- "username": self.USERNAME,
+ "email": self.EMAIL.lower(),
"password": self.PASS
}
- # no users in db so it should throw an error
- user = get_user_model().objects.create_user(self.USERNAME, '', self.PASS)
- self.post(self.login_url, data=payload, status_code=200)
- self.assertEqual('user' in self.response.json.keys(), True)
- self.assertEqual('token' in self.response.json.keys(), True)
+ # there is no users in db so it should throw error (400)
+ self.post(self.login_url, data=payload, status_code=400)
+ self.post(self.password_change_url, status_code=403)
+
+ # create user
+ user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
+
+ # test auth by email
+ self.post(self.login_url, data=payload, status_code=200)
+ self.assertEqual('key' in self.response.json.keys(), True)
+ self.token = self.response.json['key']
+
+ # test auth by email in different case
+ payload = {
+ "email": self.EMAIL.upper(),
+ "password": self.PASS
+ }
+ self.post(self.login_url, data=payload, status_code=200)
+ self.assertEqual('key' in self.response.json.keys(), True)
+ self.token = self.response.json['key']
+
+ # test inactive user
+ user.is_active = False
+ user.save()
+ self.post(self.login_url, data=payload, status_code=400)
+
+ # test wrong email/password
+ payload = {
+ "email": 't' + self.EMAIL,
+ "password": self.PASS
+ }
+ self.post(self.login_url, data=payload, status_code=400)
+
+ # test empty payload
+ self.post(self.login_url, data={}, status_code=400)
def test_password_change(self):
login_payload = {
@@ -238,7 +271,7 @@ class APITestCase1(TestCase, BaseAPITestCase):
self.post(self.login_url, data=payload, status_code=200)
def test_password_reset_with_email_in_different_case(self):
- user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL.lower(), self.PASS)
+ get_user_model().objects.create_user(self.USERNAME, self.EMAIL.lower(), self.PASS)
# call password reset in upper case
mail_count = len(mail.outbox)
@@ -247,12 +280,15 @@ class APITestCase1(TestCase, BaseAPITestCase):
self.assertEqual(len(mail.outbox), mail_count + 1)
def test_password_reset_with_invalid_email(self):
+ """
+ Invalid email should not raise error, as this would leak users
+ """
get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
# call password reset
mail_count = len(mail.outbox)
payload = {'email': 'nonexisting@email.com'}
- self.post(self.password_reset_url, data=payload, status_code=400)
+ self.post(self.password_reset_url, data=payload, status_code=200)
self.assertEqual(len(mail.outbox), mail_count)
def test_user_details(self):
@@ -271,37 +307,28 @@ class APITestCase1(TestCase, BaseAPITestCase):
self.assertEqual(user.last_name, self.response.json['last_name'])
self.assertEqual(user.email, self.response.json['email'])
- @override_settings(REST_USE_JWT=True)
- def test_user_details_jwt(self):
- user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS)
- payload = {
- 'username': self.USERNAME,
- 'password': self.PASS
- }
- self.post(self.login_url, data=payload, status_code=200)
- self.token = self.response.json['token']
- self.get(self.user_url, status_code=200)
-
- self.patch(self.user_url, data=self.BASIC_USER_DATA, status_code=200)
- user = get_user_model().objects.get(pk=user.pk)
- self.assertEqual(user.first_name, self.response.json['first_name'])
- self.assertEqual(user.last_name, self.response.json['last_name'])
- self.assertEqual(user.email, self.response.json['email'])
-
def test_registration(self):
user_count = get_user_model().objects.all().count()
# test empty payload
self.post(self.register_url, data={}, status_code=400)
- self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201)
+ result = self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201)
+ self.assertIn('key', result.data)
self.assertEqual(get_user_model().objects.all().count(), user_count + 1)
+
new_user = get_user_model().objects.latest('id')
self.assertEqual(new_user.username, self.REGISTRATION_DATA['username'])
self._login()
self._logout()
+ def test_registration_with_invalid_password(self):
+ data = self.REGISTRATION_DATA.copy()
+ data['password2'] = 'foobar'
+
+ self.post(self.register_url, data=data, status_code=400)
+
@override_settings(
ACCOUNT_EMAIL_VERIFICATION='mandatory',
ACCOUNT_EMAIL_REQUIRED=True
@@ -317,11 +344,12 @@ class APITestCase1(TestCase, BaseAPITestCase):
status_code=status.HTTP_400_BAD_REQUEST
)
- self.post(
+ result = self.post(
self.register_url,
data=self.REGISTRATION_DATA_WITH_EMAIL,
status_code=status.HTTP_201_CREATED
)
+ self.assertNotIn('key', result.data)
self.assertEqual(get_user_model().objects.all().count(), user_count + 1)
self.assertEqual(len(mail.outbox), mail_count + 1)
new_user = get_user_model().objects.latest('id')
diff --git a/rest_auth/tests/test_social.py b/rest_auth/tests/test_social.py
index ad43b7d..34c912e 100644
--- a/rest_auth/tests/test_social.py
+++ b/rest_auth/tests/test_social.py
@@ -99,7 +99,6 @@ class TestSocialAuth(TestCase, BaseAPITestCase):
# test empty payload
self.post(self.register_url, data={}, status_code=400)
-
self.post(
self.register_url,
data=self.REGISTRATION_DATA,
diff --git a/rest_auth/tests/urls.py b/rest_auth/tests/urls.py
index b80541c..d922f7f 100644
--- a/rest_auth/tests/urls.py
+++ b/rest_auth/tests/urls.py
@@ -1,4 +1,4 @@
-from django.conf.urls import patterns, url, include
+from django.conf.urls import url, include
from django.views.generic import TemplateView
from . import django_urls
@@ -11,8 +11,7 @@ from rest_auth.registration.views import SocialLoginView
class FacebookLogin(SocialLoginView):
adapter_class = FacebookOAuth2Adapter
-urlpatterns += patterns(
- '',
+urlpatterns += [
url(r'^rest-registration/', include('rest_auth.registration.urls')),
url(r'^test-admin/', include(django_urls)),
url(r'^account-email-verification-sent/$', TemplateView.as_view(),
@@ -21,4 +20,4 @@ urlpatterns += patterns(
name='account_confirm_email'),
url(r'^social-login/facebook/$', FacebookLogin.as_view(), name='fb_login'),
url(r'^accounts/', include('allauth.socialaccount.urls'))
-)
+]
diff --git a/rest_auth/urls.py b/rest_auth/urls.py
index d753c44..7a35e9b 100644
--- a/rest_auth/urls.py
+++ b/rest_auth/urls.py
@@ -1,12 +1,11 @@
-from django.conf.urls import patterns, url
+from django.conf.urls import url
from rest_auth.views import (
LoginView, LogoutView, UserDetailsView, PasswordChangeView,
PasswordResetView, PasswordResetConfirmView
)
-urlpatterns = patterns(
- '',
+urlpatterns = [
# URLs that do not require a session or valid token
url(r'^password/reset/$', PasswordResetView.as_view(),
name='rest_password_reset'),
@@ -18,4 +17,4 @@ urlpatterns = patterns(
url(r'^user/$', UserDetailsView.as_view(), name='rest_user_details'),
url(r'^password/change/$', PasswordChangeView.as_view(),
name='rest_password_change'),
-)
+]
diff --git a/rest_auth/utils.py b/rest_auth/utils.py
index 8b6e8c1..13df03e 100644
--- a/rest_auth/utils.py
+++ b/rest_auth/utils.py
@@ -10,6 +10,11 @@ def import_callable(path_or_callable):
package, attr = path_or_callable.rsplit('.', 1)
return getattr(import_module(package), attr)
+
+def default_create_token(token_model, user, serializer):
+ token, _ = token_model.objects.get_or_create(user=user)
+ return token
+
def jwt_encode(user):
try:
from rest_framework_jwt.settings import api_settings
@@ -20,4 +25,4 @@ def jwt_encode(user):
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
- return jwt_encode_handler(payload)
+ return jwt_encode_handler(payload)
\ No newline at end of file
diff --git a/rest_auth/views.py b/rest_auth/views.py
index 16c6439..331642c 100644
--- a/rest_auth/views.py
+++ b/rest_auth/views.py
@@ -7,14 +7,14 @@ from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from rest_framework.permissions import IsAuthenticated, AllowAny
-from rest_framework.authtoken.models import Token
from rest_framework.generics import RetrieveUpdateAPIView
from .app_settings import (
TokenSerializer, UserDetailsSerializer, LoginSerializer,
PasswordResetSerializer, PasswordResetConfirmSerializer,
- PasswordChangeSerializer, JWTSerializer
+ PasswordChangeSerializer, create_token
)
+from .models import TokenModel
from .utils import jwt_encode
@@ -32,7 +32,7 @@ class LoginView(GenericAPIView):
"""
permission_classes = (AllowAny,)
serializer_class = LoginSerializer
- token_model = Token
+ token_model = TokenModel
def get_response_serializer(self):
if getattr(settings, 'REST_USE_JWT', False):
@@ -43,6 +43,9 @@ class LoginView(GenericAPIView):
def login(self):
self.user = self.serializer.validated_data['user']
+ self.token = create_token(self.token_model, self.user, self.serializer)
+ if getattr(settings, 'REST_SESSION_LOGIN', True):
+ login(self.request, self.user)
if getattr(settings, 'REST_USE_JWT', False):
self.token = jwt_encode(self.user)
@@ -174,4 +177,4 @@ class PasswordChangeView(GenericAPIView):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
- return Response({"success": "New password has been saved."})
+ return Response({"success": "New password has been saved."})
\ No newline at end of file
diff --git a/setup.py b/setup.py
index bb83cde..49deb9a 100644
--- a/setup.py
+++ b/setup.py
@@ -32,6 +32,13 @@ setup(
'djangorestframework>=3.1.0',
'six>=1.9.0',
],
+ extras_require={
+ 'with_social': ['django-allauth>=0.24.1'],
+ },
+ tests_require=[
+ 'responses>=0.5.0',
+ 'django-allauth>=0.24.1',
+ ],
test_suite='runtests.runtests',
include_package_data=True,
# cmdclass={},