From 10ae7acac924258b073f7fbf47b342b1436c93bc Mon Sep 17 00:00:00 2001 From: Roman Gorbil Date: Tue, 24 Nov 2015 17:11:46 +0700 Subject: [PATCH] Rewrite registration logic --- docs/api_endpoints.rst | 13 +--- docs/configuration.rst | 8 +++ rest_auth/registration/app_settings.py | 11 ++++ rest_auth/registration/serializers.py | 50 ++++++++++++++ rest_auth/registration/views.py | 90 ++++++++------------------ 5 files changed, 98 insertions(+), 74 deletions(-) create mode 100644 rest_auth/registration/app_settings.py diff --git a/docs/api_endpoints.rst b/docs/api_endpoints.rst index 48e475e..86a30c3 100644 --- a/docs/api_endpoints.rst +++ b/docs/api_endpoints.rst @@ -51,20 +51,9 @@ Registration - /rest-auth/registration/ (POST) - username - - password1 - - password2 + - password - 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 282f326..079c710 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -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) 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..68ba702 100644 --- a/rest_auth/registration/serializers.py +++ b/rest_auth/registration/serializers.py @@ -1,6 +1,15 @@ 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 @@ -109,3 +118,44 @@ 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) + password = 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_password(self, password): + return get_adapter().clean_password(password) + + def custom_signup(self, request, user): + pass + + def save(self, request): + adapter = get_adapter() + user = adapter.new_user(request) + self.cleaned_data = self.validated_data + self.cleaned_data['password1'] = self.cleaned_data['password'] + 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/views.py b/rest_auth/registration/views.py index e700706..f7113f2 100644 --- a/rest_auth/registration/views.py +++ b/rest_auth/registration/views.py @@ -1,77 +1,41 @@ -from django.http import HttpRequest 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.registration.serializers import (SocialLoginSerializer, + VerifyEmailSerializer) +from .app_settings import RegisterSerializer from rest_auth.views import LoginView -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 +class RegisterView(CreateAPIView): + serializer_class = RegisterSerializer - Accept the following POST parameters: username, email, password - Return the REST Framework Token Object's key. - """ + 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) + return Response(TokenSerializer(user.auth_token).data, + status=status.HTTP_201_CREATED, + headers=headers) - permission_classes = (AllowAny,) - allowed_methods = ('POST', 'OPTIONS', 'HEAD') - token_model = Token - serializer_class = TokenSerializer - - def get(self, *args, **kwargs): - return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED) - - 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) + def perform_create(self, serializer): + user = serializer.save(self.request) + Token.objects.get_or_create(user=user) + complete_signup(self.request._request, user, + allauth_settings.EMAIL_VERIFICATION, + '/') + return user class VerifyEmailView(APIView, ConfirmEmailView): @@ -80,10 +44,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)