Merge pull request #151 from Tivix/pr/130

Refactored registration logic
This commit is contained in:
mariodev 2016-01-06 18:26:47 +01:00
commit 334a29c4d9
7 changed files with 117 additions and 73 deletions

View File

@ -56,16 +56,6 @@ Registration
- password2 - password2
- email - 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) - /rest-auth/registration/verify-email/ (POST)
- key - key

View File

@ -29,6 +29,14 @@ Configuration
... ...
} }
- **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_SESSION_LOGIN** - Enable session login in Login API view (default: True) - **REST_SESSION_LOGIN** - Enable session login in Login API view (default: True)

View File

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

View File

@ -1,6 +1,15 @@
from django.http import HttpRequest from django.http import HttpRequest
from django.conf import settings 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 rest_framework import serializers
from requests.exceptions import HTTPError from requests.exceptions import HTTPError
# Import is needed only if we are using social login, in which # Import is needed only if we are using social login, in which
@ -109,3 +118,57 @@ class SocialLoginSerializer(serializers.Serializer):
attrs['user'] = login.account.user attrs['user'] = login.account.user
return attrs 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()

View File

@ -1,77 +1,42 @@
from django.http import HttpRequest
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.generics import CreateAPIView
from rest_framework import status from rest_framework import status
from rest_framework.authtoken.models import Token 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.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.app_settings import TokenSerializer
from rest_auth.registration.serializers import SocialLoginSerializer from rest_auth.registration.serializers import (SocialLoginSerializer,
VerifyEmailSerializer)
from .app_settings import RegisterSerializer
from rest_auth.views import LoginView from rest_auth.views import LoginView
class RegisterView(APIView, SignupView): class RegisterView(CreateAPIView):
""" serializer_class = RegisterSerializer
Accepts the credentials and creates a new user permission_classes = (AllowAny, )
if user does not exist already
Return the REST Token if the credentials are valid and authenticated.
Calls allauth complete_signup method
Accept the following POST parameters: username, email, password def create(self, request, *args, **kwargs):
Return the REST Framework Token Object's key. 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)
return Response(TokenSerializer(user.auth_token).data,
status=status.HTTP_201_CREATED,
headers=headers)
permission_classes = (AllowAny,) def perform_create(self, serializer):
allowed_methods = ('POST', 'OPTIONS', 'HEAD') user = serializer.save(self.request)
token_model = Token Token.objects.get_or_create(user=user)
serializer_class = TokenSerializer complete_signup(self.request._request, user,
allauth_settings.EMAIL_VERIFICATION,
def get(self, *args, **kwargs): None)
return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED) return user
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)
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 = self.user_serializer_class(instance=self.user)
serializer = self.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)
class VerifyEmailView(APIView, ConfirmEmailView): class VerifyEmailView(APIView, ConfirmEmailView):
@ -80,10 +45,12 @@ class VerifyEmailView(APIView, ConfirmEmailView):
allowed_methods = ('POST', 'OPTIONS', 'HEAD') allowed_methods = ('POST', 'OPTIONS', 'HEAD')
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED) raise MethodNotAllowed('GET')
def post(self, request, *args, **kwargs): 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 = self.get_object()
confirmation.confirm(self.request) confirmation.confirm(self.request)
return Response({'message': 'ok'}, status=status.HTTP_200_OK) return Response({'message': 'ok'}, status=status.HTTP_200_OK)

View File

@ -318,6 +318,12 @@ class APITestCase1(TestCase, BaseAPITestCase):
self._login() self._login()
self._logout() 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( @override_settings(
ACCOUNT_EMAIL_VERIFICATION='mandatory', ACCOUNT_EMAIL_VERIFICATION='mandatory',
ACCOUNT_EMAIL_REQUIRED=True ACCOUNT_EMAIL_REQUIRED=True

View File

@ -99,7 +99,6 @@ class TestSocialAuth(TestCase, BaseAPITestCase):
# test empty payload # test empty payload
self.post(self.register_url, data={}, status_code=400) self.post(self.register_url, data={}, status_code=400)
self.post( self.post(
self.register_url, self.register_url,
data=self.REGISTRATION_DATA, data=self.REGISTRATION_DATA,