mirror of
https://github.com/Tivix/django-rest-auth.git
synced 2024-11-22 09:06:40 +03:00
django-registration replacement
- remove django-registration references - integrate with django-allauth - move all registration stuff to separated app - update unit tests
This commit is contained in:
parent
640ce1b970
commit
f14b3b03f7
0
rest_auth/registration/__init__.py
Normal file
0
rest_auth/registration/__init__.py
Normal file
17
rest_auth/registration/serializers.py
Normal file
17
rest_auth/registration/serializers.py
Normal file
|
@ -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
|
10
rest_auth/registration/urls.py
Normal file
10
rest_auth/registration/urls.py
Normal file
|
@ -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<activation_key>\w+)/$', VerifyEmail.as_view(),
|
||||
name='verify_email'),
|
||||
)
|
||||
|
44
rest_auth/registration/views.py
Normal file
44
rest_auth/registration/views.py
Normal file
|
@ -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
|
|
@ -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():
|
||||
|
|
|
@ -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):
|
||||
|
||||
"""
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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<uid>[0-9A-Za-z_\-]+)/(?P<token>[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<activation_key>\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))
|
||||
)
|
||||
|
|
|
@ -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):
|
||||
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue
Block a user