diff --git a/rest_auth/registration/__init__.py b/rest_auth/registration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rest_auth/registration/serializers.py b/rest_auth/registration/serializers.py new file mode 100644 index 0000000..f041da8 --- /dev/null +++ b/rest_auth/registration/serializers.py @@ -0,0 +1,17 @@ +from rest_framework import serializers +from django.contrib.auth import get_user_model + + +class UserRegistrationSerializer(serializers.ModelSerializer): + + """ + Serializer for Django User model and most of its fields. + """ + + class Meta: + model = get_user_model() + fields = ('username', 'password', 'email', 'first_name', 'last_name') + + +class VerifyEmailSerializer(serializers.Serializer): + pass diff --git a/rest_auth/registration/urls.py b/rest_auth/registration/urls.py new file mode 100644 index 0000000..615b0a9 --- /dev/null +++ b/rest_auth/registration/urls.py @@ -0,0 +1,10 @@ +from django.conf.urls import patterns, url + +from .views import Register, VerifyEmail + +urlpatterns = patterns('', + url(r'^$', Register.as_view(), name='rest_register'), + url(r'^verify-email/(?P\w+)/$', VerifyEmail.as_view(), + name='verify_email'), +) + diff --git a/rest_auth/registration/views.py b/rest_auth/registration/views.py new file mode 100644 index 0000000..f812e1d --- /dev/null +++ b/rest_auth/registration/views.py @@ -0,0 +1,44 @@ +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.permissions import AllowAny +from rest_framework import status + +from allauth.account.views import SignupView +from allauth.account.utils import complete_signup +from allauth.account import app_settings + +from rest_auth.serializers import UserDetailsSerializer + + +class Register(APIView, SignupView): + + permission_classes = (AllowAny,) + user_serializer_class = UserDetailsSerializer + + def form_valid(self, form): + self.user = form.save(self.request) + return complete_signup(self.request, self.user, + app_settings.EMAIL_VERIFICATION, + self.get_success_url()) + + def post(self, request, *args, **kwargs): + self.initial = {} + self.request.POST = self.request.DATA.copy() + 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 = self.user_serializer_class(instance=self.user) + 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) + + +class VerifyEmail(APIView): + pass diff --git a/rest_auth/runtests.py b/rest_auth/runtests.py index 01a3797..3d7e413 100644 --- a/rest_auth/runtests.py +++ b/rest_auth/runtests.py @@ -1,55 +1,13 @@ #This file mainly exists to allow python setup.py test to work. -import os, sys +import os +import sys + os.environ['DJANGO_SETTINGS_MODULE'] = 'test_settings' test_dir = os.path.dirname(__file__) sys.path.insert(0, test_dir) from django.test.utils import get_runner from django.conf import settings -from django.contrib.auth.models import User -from django.contrib.sites.models import RequestSite -from django.contrib.sites.models import Site -from django.db import models - -from rest_framework.serializers import _resolve_model -from registration.models import RegistrationProfile -from registration.backends.default.views import RegistrationView as BaseRegistrationView -from registration import signals - -""" -create user profile model -""" -class UserProfile(models.Model): - user = models.ForeignKey(User, unique=True) - newsletter_subscribe = models.BooleanField(default=False) - - class Meta: - app_label = 'rest_auth' - - -""" -overwrite register to avoid sending email -""" -class RegistrationView(BaseRegistrationView): - def register(self, request, **cleaned_data): - username, email, password = cleaned_data['username'], cleaned_data['email'], cleaned_data['password1'] - if Site._meta.installed: - site = Site.objects.get_current() - else: - site = RequestSite(request) - new_user = RegistrationProfile.objects.create_inactive_user(username, email, - password, site, send_email=False) - signals.user_registered.send(sender=self.__class__, - user=new_user, - request=request) - - # create user profile - profile_model_path = getattr(settings, 'REST_PROFILE_MODULE', None) - if profile_model_path: - user_profile_model = _resolve_model(profile_model_path) - user_profile_model.objects.create(user=new_user) - - return new_user def runtests(): diff --git a/rest_auth/serializers.py b/rest_auth/serializers.py index af4725e..b2e708a 100644 --- a/rest_auth/serializers.py +++ b/rest_auth/serializers.py @@ -34,17 +34,6 @@ class UserDetailsSerializer(serializers.ModelSerializer): fields = ('username', 'email', 'first_name', 'last_name') -class UserRegistrationSerializer(serializers.ModelSerializer): - - """ - Serializer for Django User model and most of its fields. - """ - - class Meta: - model = get_user_model() - fields = ('username', 'password', 'email', 'first_name', 'last_name') - - class DynamicFieldsModelSerializer(serializers.ModelSerializer): """ diff --git a/rest_auth/test_settings.py b/rest_auth/test_settings.py index 1b6f82e..175f989 100644 --- a/rest_auth/test_settings.py +++ b/rest_auth/test_settings.py @@ -1,5 +1,6 @@ import django -import os, sys +import os +import sys PROJECT_ROOT = os.path.abspath(os.path.split(os.path.split(__file__)[0])[0]) ROOT_URLCONF = 'urls' @@ -37,11 +38,14 @@ INSTALLED_APPS = [ 'django.contrib.sitemaps', 'django.contrib.staticfiles', + 'allauth', + 'allauth.account', + 'rest_framework', 'rest_framework.authtoken', - 'registration', 'rest_auth', + 'rest_auth.registration' ] SECRET_KEY = "38dh*skf8sjfhs287dh&^hd8&3hdg*j2&sd" diff --git a/rest_auth/tests.py b/rest_auth/tests.py index 2ad4945..1a78b5e 100644 --- a/rest_auth/tests.py +++ b/rest_auth/tests.py @@ -9,7 +9,6 @@ from django.contrib.auth.models import User from django.contrib.auth import get_user_model from django.core import mail -from registration.models import RegistrationProfile from rest_framework.serializers import _resolve_model @@ -113,7 +112,6 @@ class BaseAPITestCase(object): # ----------------------- - class APITestCase1(TestCase, BaseAPITestCase): """ Case #1: @@ -129,16 +127,12 @@ class APITestCase1(TestCase, BaseAPITestCase): REGISTRATION_VIEW = 'rest_auth.runtests.RegistrationView' # data without user profile - BASIC_REGISTRATION_DATA = { + REGISTRATION_DATA = { "username": USERNAME, - "password": PASS, - "email": EMAIL + "password1": PASS, + "password2": PASS } - # data with user profile - REGISTRATION_DATA = BASIC_REGISTRATION_DATA.copy() - REGISTRATION_DATA['newsletter_subscribe'] = False - BASIC_USER_DATA = { 'first_name': "John", 'last_name': 'Smith', @@ -191,7 +185,7 @@ class APITestCase1(TestCase, BaseAPITestCase): # test wrong username/password payload = { - "username": self.USERNAME+'?', + "username": self.USERNAME + '?', "password": self.PASS } self.post(self.login_url, data=payload, status_code=401) @@ -199,13 +193,12 @@ class APITestCase1(TestCase, BaseAPITestCase): # test empty payload self.post(self.login_url, data={}, status_code=400) - def test_password_change(self): login_payload = { "username": self.USERNAME, "password": self.PASS } - user = User.objects.create_user(self.USERNAME, '', self.PASS) + User.objects.create_user(self.USERNAME, '', self.PASS) self.post(self.login_url, data=login_payload, status_code=200) self.token = self.response.json['key'] @@ -234,61 +227,6 @@ class APITestCase1(TestCase, BaseAPITestCase): # send empty payload self.post(self.password_change_url, data={}, status_code=400) - def test_registration(self): - user_count = User.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) - self.assertEqual(User.objects.all().count(), user_count+1) - new_user = get_user_model().objects.latest('id') - - if self.REGISTRATION_VIEW: - activation_key = RegistrationProfile.objects.latest('id').activation_key - verify_url = reverse('verify_email', - kwargs={'activation_key': activation_key}) - - # new user at this point shouldn't be active - self.assertEqual(new_user.is_active, False) - - # let's active new user and check is_active flag - self.get(verify_url) - new_user = get_user_model().objects.latest('id') - self.assertEqual(new_user.is_active, True) - if self.user_profile_model: - user_profile = self.user_profile_model.objects.get(user=new_user) - self.assertIsNotNone(user_profile) - else: - self.assertEqual(new_user.is_active, True) - - def test_registration_without_profile_data(self): - user_count = User.objects.all().count() - - self.post(self.register_url, data=self.BASIC_REGISTRATION_DATA, - status_code=201) - self.assertEqual(User.objects.all().count(), user_count+1) - new_user = get_user_model().objects.latest('id') - - if self.REGISTRATION_VIEW: - activation_key = RegistrationProfile.objects.latest('id').activation_key - verify_url = reverse('verify_email', - kwargs={'activation_key': activation_key}) - - # new user at this point shouldn't be active - self.assertEqual(new_user.is_active, False) - - # let's active new user and check is_active flag - self.get(verify_url) - new_user = get_user_model().objects.latest('id') - self.assertEqual(new_user.is_active, True) - if self.user_profile_model: - user_profile = self.user_profile_model.objects.get(user=new_user) - self.assertIsNotNone(user_profile) - else: - self.assertEqual(new_user.is_active, True) - - def test_password_reset(self): user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASS) @@ -296,7 +234,7 @@ class APITestCase1(TestCase, BaseAPITestCase): mail_count = len(mail.outbox) payload = {'email': self.EMAIL} self.post(self.password_reset_url, data=payload) - self.assertEqual(len(mail.outbox), mail_count+1) + self.assertEqual(len(mail.outbox), mail_count + 1) url_kwargs = self.generate_uid_and_token(user) @@ -340,7 +278,6 @@ class APITestCase1(TestCase, BaseAPITestCase): self.assertEqual(user.last_name, self.response.json['last_name']) self.assertEqual(user.email, self.response.json['email']) - def generate_uid_and_token(self, user): result = {} from django.utils.encoding import force_bytes @@ -355,30 +292,13 @@ class APITestCase1(TestCase, BaseAPITestCase): result['token'] = default_token_generator.make_token(user) return result + def test_registration(self): + user_count = User.objects.all().count() -class APITestCase2(APITestCase1): - """ - Case #2: - - user profile: not defined - - custom registration backend: not defined - """ - PROFILE_MODEL = None + # test empty payload + self.post(self.register_url, data={}, status_code=400) - -class APITestCase3(APITestCase1): - """ - Case #3: - - user profile: defined - - custom registration backend: not defined - """ - REGISTRATION_VIEW = None - - -class APITestCase4(APITestCase1): - """ - Case #4: - - user profile: not defined - - custom registration backend: not defined - """ - PROFILE_MODEL = None - REGISTRATION_VIEW = None + self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201) + self.assertEqual(User.objects.all().count(), user_count + 1) + new_user = get_user_model().objects.latest('id') + self.assertEqual(new_user.username, self.REGISTRATION_DATA['username']) diff --git a/rest_auth/urls.py b/rest_auth/urls.py index b51bc1e..b1abfe0 100644 --- a/rest_auth/urls.py +++ b/rest_auth/urls.py @@ -1,22 +1,18 @@ from django.conf import settings from django.conf.urls import patterns, url, include -from rest_auth.views import Login, Logout, Register, UserDetails, \ - PasswordChange, PasswordReset, VerifyEmail, PasswordResetConfirm +from rest_auth.views import Login, Logout, UserDetails, \ + PasswordChange, PasswordReset, PasswordResetConfirm urlpatterns = patterns('rest_auth.views', # URLs that do not require a session or valid token - url(r'^register/$', Register.as_view(), - name='rest_register'), url(r'^password/reset/$', PasswordReset.as_view(), name='rest_password_reset'), url(r'^password/reset/confirm/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', PasswordResetConfirm.as_view( ), name='rest_password_reset_confirm'), url(r'^login/$', Login.as_view(), name='rest_login'), - url(r'^verify-email/(?P\w+)/$', - VerifyEmail.as_view(), name='verify_email'), # URLs that require a user to be logged in with a valid # session / token. @@ -29,4 +25,7 @@ urlpatterns = patterns('rest_auth.views', if getattr(settings, 'IS_TEST', False): from django.contrib.auth.tests import urls - urlpatterns += patterns('', url(r'^test-admin/', include(urls))) + urlpatterns += patterns('', + url(r'^rest-registration/', include('registration.urls')), + url(r'^test-admin/', include(urls)) + ) diff --git a/rest_auth/views.py b/rest_auth/views.py index 50ee4e5..9bc5b20 100644 --- a/rest_auth/views.py +++ b/rest_auth/views.py @@ -7,7 +7,6 @@ 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.core.exceptions import ObjectDoesNotExist from rest_framework import status from rest_framework.views import APIView @@ -19,17 +18,11 @@ from rest_framework.authentication import SessionAuthentication, \ TokenAuthentication from rest_framework.authtoken.models import Token -from registration.models import RegistrationProfile -from registration import signals -from registration.views import ActivationView - from rest_auth.utils import construct_modules_and_import -from rest_auth.models import * from rest_auth.serializers import (TokenSerializer, UserDetailsSerializer, - LoginSerializer, UserRegistrationSerializer, + LoginSerializer, SetPasswordSerializer, PasswordResetSerializer, UserUpdateSerializer, - get_user_registration_profile_serializer, get_user_profile_serializer, - get_user_profile_update_serializer) + get_user_profile_serializer, get_user_profile_update_serializer) def get_user_profile_model(): @@ -40,16 +33,6 @@ def get_user_profile_model(): return _resolve_model(user_profile_path) -def get_registration_backend(): - # Get the REST Registration Backend for django-registration - registration_backend = getattr(settings, 'REST_REGISTRATION_BACKEND', - 'registration.backends.simple.views.RegistrationView') - - # Get the REST REGISTRATION BACKEND class from the setting value via above - # method - return construct_modules_and_import(registration_backend) - - class LoggedInRESTAPIView(APIView): authentication_classes = ((SessionAuthentication, TokenAuthentication)) permission_classes = ((IsAuthenticated,)) @@ -125,53 +108,6 @@ class Logout(LoggedInRESTAPIView): status=status.HTTP_200_OK) -class Register(LoggedOutRESTAPIView, GenericAPIView): - - """ - Registers a new Django User object by accepting required field values. - - Accepts the following POST parameters: - Required: username, password, email - Optional: first_name & last_name for User object and UserProfile fields - Returns the newly created User object including REST Framework Token key. - """ - - serializer_class = UserRegistrationSerializer - - def get_profile_serializer_class(self): - return get_user_registration_profile_serializer() - - def post(self, request): - # Create serializers with request.DATA - serializer = self.serializer_class(data=request.DATA) - profile_serializer_class = self.get_profile_serializer_class() - profile_serializer = profile_serializer_class(data=request.DATA) - - if serializer.is_valid() and profile_serializer.is_valid(): - # Change the password key to password1 so that RESTRegistrationView - # can accept the data - serializer.data['password1'] = serializer.data.pop('password') - - # TODO: Make this customizable backend via settings. - # Call RESTRegistrationView().register to create new Django User - # and UserProfile models - data = serializer.data.copy() - data.update(profile_serializer.data) - - RESTRegistrationView = get_registration_backend() - RESTRegistrationView().register(request, **data) - - # Return the User object with Created HTTP status - return Response(UserDetailsSerializer(serializer.data).data, - status=status.HTTP_201_CREATED) - - else: - return Response({ - 'user': serializer.errors, - 'profile': profile_serializer.errors}, - status=status.HTTP_400_BAD_REQUEST) - - class UserDetails(LoggedInRESTAPIView, GenericAPIView): """ @@ -328,37 +264,6 @@ class PasswordResetConfirm(LoggedOutRESTAPIView, GenericAPIView): return Response({"errors": "Couldn\'t find the user from uid."}, status=status.HTTP_400_BAD_REQUEST) -class VerifyEmail(LoggedOutRESTAPIView, GenericAPIView): - - """ - Verifies the email of the user through their activation_key. - - Accepts activation_key django argument: key from activation email. - Returns the success/fail message. - """ - - model = RegistrationProfile - - def get(self, request, activation_key=None): - # Get the user registration profile with the activation key - target_user = RegistrationProfile.objects.activate_user(activation_key) - - if target_user: - # Send the activation signal - signals.user_activated.send(sender=ActivationView.__class__, - user=target_user, - request=request) - - # Return the success message with OK HTTP status - ret_msg = "User {0}'s account was successfully activated!".format( - target_user.username) - return Response({"success": ret_msg}, status=status.HTTP_200_OK) - - else: - ret_msg = "The account was not able to be activated or already activated, please contact support." - return Response({"errors": ret_msg}, status=status.HTTP_400_BAD_REQUEST) - - class PasswordChange(LoggedInRESTAPIView, GenericAPIView): """ diff --git a/setup.py b/setup.py index a247e92..8bbfb04 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ setup( zip_safe=False, install_requires=[ 'Django>=1.5.0', - 'django-registration>=1.0', + 'django-allauth>=0.18.0', 'djangorestframework>=2.3.13', ], test_suite='rest_auth.runtests.runtests',