From f43a6b8d585b6c1669d7071b2e97670a287ac032 Mon Sep 17 00:00:00 2001 From: Nikolay Golub Date: Thu, 23 Jul 2015 22:22:39 +0300 Subject: [PATCH 01/11] move SocialAccount population to the separate method in the SocialLoginSerializer. It makes easier to get the correct signup for custom user models, because application can subclass SocialLoginSeriaLizer and add required fields to the instance. --- rest_auth/registration/serializers.py | 41 ++++++++++++++++++--------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/rest_auth/registration/serializers.py b/rest_auth/registration/serializers.py index e3c69ff..6e0d090 100644 --- a/rest_auth/registration/serializers.py +++ b/rest_auth/registration/serializers.py @@ -5,36 +5,49 @@ from allauth.socialaccount.helpers import complete_social_login class SocialLoginSerializer(serializers.Serializer): - access_token = serializers.CharField(required=True) + def _get_request(self): + request = self.context.get('request') + if not isinstance(request, HttpRequest): + request = request._request + return request + + def get_social_account(self, adapter, app, token, response): + """ + + :param adapter: allauth.socialaccount Adapter subclass. Usually OAuthAdapter or Auth2Adapter + :param app: `allauth.socialaccount.SocialApp` instance + :param token: `allauth.socialaccount.SocialToken` instance + :param response: Provider's response for OAuth1. Not used in the + :return: :return: A populated instance of the `allauth.socialaccount.SocialLogin` instance + """ + request = self._get_request() + social_login = adapter.complete_login(request, app, token, response=response) + social_login.token = token + return social_login + def validate(self, attrs): access_token = attrs.get('access_token') view = self.context.get('view') - request = self.context.get('request') - if not isinstance(request, HttpRequest): - request = request._request + request = self._get_request() if not view: raise serializers.ValidationError( 'View is not defined, pass it as a context variable' ) - self.adapter_class = getattr(view, 'adapter_class', None) - - if not self.adapter_class: + adapter_class = getattr(view, 'adapter_class', None) + if not adapter_class: raise serializers.ValidationError('Define adapter_class in view') - self.adapter = self.adapter_class() - app = self.adapter.get_provider().get_app(request) - token = self.adapter.parse_token({'access_token': access_token}) + adapter = adapter_class() + app = adapter.get_provider().get_app(request) + token = adapter.parse_token({'access_token': access_token}) token.app = app try: - login = self.adapter.complete_login(request, app, token, - response=access_token) - - login.token = token + login = self.get_social_account(adapter, app, token, access_token) complete_social_login(request, login) except HTTPError: raise serializers.ValidationError('Incorrect value') From a9d2a24011f0ebe1a3b34805da1d051630a876fa Mon Sep 17 00:00:00 2001 From: Nikolay Golub Date: Thu, 23 Jul 2015 22:33:48 +0300 Subject: [PATCH 02/11] Make names more consistent --- rest_auth/registration/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_auth/registration/serializers.py b/rest_auth/registration/serializers.py index 6e0d090..9fb3ec7 100644 --- a/rest_auth/registration/serializers.py +++ b/rest_auth/registration/serializers.py @@ -13,7 +13,7 @@ class SocialLoginSerializer(serializers.Serializer): request = request._request return request - def get_social_account(self, adapter, app, token, response): + def get_social_login(self, adapter, app, token, response): """ :param adapter: allauth.socialaccount Adapter subclass. Usually OAuthAdapter or Auth2Adapter @@ -47,7 +47,7 @@ class SocialLoginSerializer(serializers.Serializer): token.app = app try: - login = self.get_social_account(adapter, app, token, access_token) + login = self.get_social_login(adapter, app, token, access_token) complete_social_login(request, login) except HTTPError: raise serializers.ValidationError('Incorrect value') From 8ea935ef4027500c54f8a4ad2e2efda6dceeb24b Mon Sep 17 00:00:00 2001 From: Philippe Luickx Date: Fri, 7 Aug 2015 11:25:40 +0300 Subject: [PATCH 03/11] conditional import --- rest_auth/registration/serializers.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rest_auth/registration/serializers.py b/rest_auth/registration/serializers.py index 9fb3ec7..4f0f0aa 100644 --- a/rest_auth/registration/serializers.py +++ b/rest_auth/registration/serializers.py @@ -1,7 +1,12 @@ from django.http import HttpRequest from rest_framework import serializers from requests.exceptions import HTTPError -from allauth.socialaccount.helpers import complete_social_login +# 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: + pass class SocialLoginSerializer(serializers.Serializer): From 74f2ffec7f7207c9651901db9323e3320e9e6b71 Mon Sep 17 00:00:00 2001 From: Philippe Luickx Date: Fri, 7 Aug 2015 13:26:57 +0300 Subject: [PATCH 04/11] now also accepting authorization codes from social login (e.g. facebook) --- docs/api_endpoints.rst | 1 + rest_auth/registration/serializers.py | 42 +++++++++++++++++++++++++-- rest_auth/registration/views.py | 40 +++++++++++++++++++++---- 3 files changed, 76 insertions(+), 7 deletions(-) diff --git a/docs/api_endpoints.rst b/docs/api_endpoints.rst index 05e8691..1b55bb3 100644 --- a/docs/api_endpoints.rst +++ b/docs/api_endpoints.rst @@ -73,3 +73,4 @@ Basing on example from installation section :doc:`Installation ` - /rest-auth/facebook/ (POST) - access_token + - code diff --git a/rest_auth/registration/serializers.py b/rest_auth/registration/serializers.py index 4f0f0aa..49c3123 100644 --- a/rest_auth/registration/serializers.py +++ b/rest_auth/registration/serializers.py @@ -10,7 +10,8 @@ except ImportError: class SocialLoginSerializer(serializers.Serializer): - access_token = serializers.CharField(required=True) + access_token = serializers.CharField(required=False) + code = serializers.CharField(required=False) def _get_request(self): request = self.context.get('request') @@ -33,7 +34,6 @@ class SocialLoginSerializer(serializers.Serializer): return social_login def validate(self, attrs): - access_token = attrs.get('access_token') view = self.context.get('view') request = self._get_request() @@ -48,6 +48,44 @@ class SocialLoginSerializer(serializers.Serializer): adapter = adapter_class() app = adapter.get_provider().get_app(request) + + # More info on code vs access_token + # http://stackoverflow.com/questions/8666316/facebook-oauth-2-0-code-and-token + + # Case 1: We received the access_token + if('access_token' in attrs): + access_token = attrs.get('access_token') + + # Case 2: We received the authorization code + elif('code' in attrs): + self.callback_url = getattr(view, 'callback_url', None) + self.client_class = getattr(view, 'client_class', None) + + if not self.callback_url: + raise serializers.ValidationError( + 'Define callback_url in view' + ) + if not self.client_class: + raise serializers.ValidationError( + 'Define client_class in view' + ) + + code = attrs.get('code') + + provider = self.adapter.get_provider() + scope = provider.get_scope(request) + client = self.client_class( + request, + app.client_id, + app.secret, + self.adapter.access_token_method, + self.adapter.access_token_url, + self.callback_url, + scope + ) + token = client.get_access_token(code) + access_token = token['access_token'] + token = adapter.parse_token({'access_token': access_token}) token.app = app diff --git a/rest_auth/registration/views.py b/rest_auth/registration/views.py index 485881c..d462b3a 100644 --- a/rest_auth/registration/views.py +++ b/rest_auth/registration/views.py @@ -3,21 +3,33 @@ from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.permissions import AllowAny from rest_framework import status +from rest_framework.authtoken.models import Token from allauth.account.views import SignupView, ConfirmEmailView from allauth.account.utils import complete_signup from allauth.account import app_settings -from rest_auth.app_settings import UserDetailsSerializer +from rest_auth.app_settings import UserDetailsSerializer, TokenSerializer from rest_auth.registration.serializers import SocialLoginSerializer from rest_auth.views import Login class Register(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 + + Accept the following POST parameters: username, email, password + Return the REST Framework Token Object's key. + """ permission_classes = (AllowAny,) - user_serializer_class = UserDetailsSerializer + # user_serializer_class = UserDetailsSerializer 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) @@ -27,6 +39,9 @@ class Register(APIView, SignupView): 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: @@ -47,7 +62,8 @@ class Register(APIView, SignupView): return self.get_response_with_errors() def get_response(self): - serializer = self.user_serializer_class(instance=self.user) + # serializer = self.user_serializer_class(instance=self.user) + serializer = self.serializer_class(instance=self.token) return Response(serializer.data, status=status.HTTP_201_CREATED) def get_response_with_errors(self): @@ -72,11 +88,25 @@ class VerifyEmail(APIView, ConfirmEmailView): class SocialLogin(Login): """ class used for social authentications - example usage for facebook - + example usage for facebook with access_token + ------------- from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter + class FacebookLogin(SocialLogin): adapter_class = FacebookOAuth2Adapter + ------------- + + example usage for facebook with code + + ------------- + from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter + from allauth.socialaccount.providers.oauth2.client import OAuth2Client + + class FacebookLogin(SocialLogin): + adapter_class = FacebookOAuth2Adapter + client_class = OAuth2Client + callback_url = 'localhost:8000' + ------------- """ serializer_class = SocialLoginSerializer From bd193a1401a5e2a243f300d8db8ecca82d6e6621 Mon Sep 17 00:00:00 2001 From: Philippe Luickx Date: Fri, 7 Aug 2015 13:43:21 +0300 Subject: [PATCH 05/11] you can now login with email and password, without username --- rest_auth/serializers.py | 61 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/rest_auth/serializers.py b/rest_auth/serializers.py index 8a11084..53fbcbc 100644 --- a/rest_auth/serializers.py +++ b/rest_auth/serializers.py @@ -1,4 +1,4 @@ -from django.contrib.auth import get_user_model +from django.contrib.auth import get_user_model, authenticate from django.conf import settings from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm try: @@ -7,24 +7,75 @@ except: # make compatible with django 1.5 from django.utils.http import base36_to_int as uid_decoder from django.contrib.auth.tokens import default_token_generator +from django.utils.translation import ugettext_lazy as _ -from rest_framework import serializers +from rest_framework import serializers, exceptions from rest_framework.authtoken.models import Token from rest_framework.authtoken.serializers import AuthTokenSerializer from rest_framework.exceptions import ValidationError -class LoginSerializer(AuthTokenSerializer): +class LoginSerializer(serializers.Serializer): + username = serializers.CharField(required=False) + email = serializers.EmailField(required=False) + password = serializers.CharField(style={'input_type': 'password'}) def validate(self, attrs): - attrs = super(LoginSerializer, self).validate(attrs) + username = attrs.get('username') + email = attrs.get('email') + password = attrs.get('password') + + 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) + # 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) + # 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) + + else: + msg = _('Must include "username" and "password".') + raise exceptions.ValidationError(msg) + + # Did we get back an active user? + if user: + if not user.is_active: + msg = _('User account is disabled.') + raise exceptions.ValidationError(msg) + else: + msg = _('Unable to log in with provided credentials.') + raise exceptions.ValidationError(msg) + + # If required, is the email verified? if 'rest_auth.registration' in settings.INSTALLED_APPS: from allauth.account import app_settings if app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY: - user = attrs['user'] email_address = user.emailaddress_set.get(email=user.email) if not email_address.verified: raise serializers.ValidationError('E-mail is not verified.') + + attrs['user'] = user return attrs From 4a3ea85f44f80af12ebd12eafead15680419fd72 Mon Sep 17 00:00:00 2001 From: Philippe Luickx Date: Fri, 7 Aug 2015 13:46:18 +0300 Subject: [PATCH 06/11] well, that was an easy cleanup, now it is nice and consistent --- rest_auth/registration/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rest_auth/registration/views.py b/rest_auth/registration/views.py index d462b3a..55852dd 100644 --- a/rest_auth/registration/views.py +++ b/rest_auth/registration/views.py @@ -26,7 +26,6 @@ class Register(APIView, SignupView): """ permission_classes = (AllowAny,) - # user_serializer_class = UserDetailsSerializer allowed_methods = ('POST', 'OPTIONS', 'HEAD') token_model = Token serializer_class = TokenSerializer From ad9400850371fe8a494acd7be57c9950e0424aa7 Mon Sep 17 00:00:00 2001 From: Philippe Luickx Date: Fri, 7 Aug 2015 13:54:45 +0300 Subject: [PATCH 07/11] appending all views with View --- docs/configuration.rst | 12 ++++++------ docs/faq.rst | 2 +- docs/installation.rst | 6 +++--- rest_auth/registration/serializers.py | 2 +- rest_auth/registration/urls.py | 6 +++--- rest_auth/registration/views.py | 12 ++++++------ rest_auth/test_urls.py | 4 ++-- rest_auth/urls.py | 16 ++++++++-------- rest_auth/views.py | 12 ++++++------ 9 files changed, 36 insertions(+), 36 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index f04d86d..ed0d785 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -6,17 +6,17 @@ Configuration You can define your custom serializers for each endpoint without overriding urls and views by adding ``REST_AUTH_SERIALIZERS`` dictionary in your django settings. Possible key values: - - LOGIN_SERIALIZER - serializer class in ``rest_auth.views.Login``, default value ``rest_auth.serializers.LoginSerializer`` + - LOGIN_SERIALIZER - serializer class in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.LoginSerializer`` - - TOKEN_SERIALIZER - response for successful authentication in ``rest_auth.views.Login``, default value ``rest_auth.serializers.TokenSerializer`` + - TOKEN_SERIALIZER - response for successful authentication in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.TokenSerializer`` - - USER_DETAILS_SERIALIZER - serializer class in ``rest_auth.views.UserDetails``, default value ``rest_auth.serializers.UserDetailsSerializer`` + - USER_DETAILS_SERIALIZER - serializer class in ``rest_auth.views.UserDetailsView``, default value ``rest_auth.serializers.UserDetailsSerializer`` - - PASSWORD_RESET_SERIALIZER - serializer class in ``rest_auth.views.PasswordReset``, default value ``rest_auth.serializers.PasswordResetSerializer`` + - PASSWORD_RESET_SERIALIZER - serializer class in ``rest_auth.views.PasswordResetView``, default value ``rest_auth.serializers.PasswordResetSerializer`` - - PASSWORD_RESET_CONFIRM_SERIALIZER - serializer class in ``rest_auth.views.PasswordResetConfirm``, default value ``rest_auth.serializers.PasswordResetConfirmSerializer`` + - PASSWORD_RESET_CONFIRM_SERIALIZER - serializer class in ``rest_auth.views.PasswordResetConfirmView``, default value ``rest_auth.serializers.PasswordResetConfirmSerializer`` - - PASSWORD_CHANGE_SERIALIZER - serializer class in ``rest_auth.views.PasswordChange``, default value ``rest_auth.serializers.PasswordChangeSerializer`` + - PASSWORD_CHANGE_SERIALIZER - serializer class in ``rest_auth.views.PasswordChangeView``, default value ``rest_auth.serializers.PasswordChangeSerializer`` Example configuration: diff --git a/docs/faq.rst b/docs/faq.rst index 0a231bb..5faeee6 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -31,7 +31,7 @@ FAQ # custom fields for user company_name = models.CharField(max_length=100) - To allow update user details within one request send to rest_auth.views.UserDetails view, create serializer like this: + To allow update user details within one request send to rest_auth.views.UserDetailsView view, create serializer like this: .. code-block:: python diff --git a/docs/installation.rst b/docs/installation.rst index f15215a..cd190a4 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -91,14 +91,14 @@ Using ``django-allauth``, ``django-rest-auth`` provides helpful class for creati 3. Add Social Application in django admin panel -4. Create new view as a subclass of ``rest_auth.registration.views.SocialLogin`` with ``FacebookOAuth2Adapter`` adapter as an attribute: +4. Create new view as a subclass of ``rest_auth.registration.views.SocialLoginView`` with ``FacebookOAuth2Adapter`` adapter as an attribute: .. code-block:: python from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter - from rest_auth.registration.views import SocialLogin + from rest_auth.registration.views import SocialLoginView - class FacebookLogin(SocialLogin): + class FacebookLogin(SocialLoginView): adapter_class = FacebookOAuth2Adapter 5. Create url for FacebookLogin view: diff --git a/rest_auth/registration/serializers.py b/rest_auth/registration/serializers.py index 49c3123..d77eaac 100644 --- a/rest_auth/registration/serializers.py +++ b/rest_auth/registration/serializers.py @@ -26,7 +26,7 @@ class SocialLoginSerializer(serializers.Serializer): :param app: `allauth.socialaccount.SocialApp` instance :param token: `allauth.socialaccount.SocialToken` instance :param response: Provider's response for OAuth1. Not used in the - :return: :return: A populated instance of the `allauth.socialaccount.SocialLogin` instance + :return: :return: A populated instance of the `allauth.socialaccount.SocialLoginView` instance """ request = self._get_request() social_login = adapter.complete_login(request, app, token, response=response) diff --git a/rest_auth/registration/urls.py b/rest_auth/registration/urls.py index 838070e..abdd8b5 100644 --- a/rest_auth/registration/urls.py +++ b/rest_auth/registration/urls.py @@ -1,12 +1,12 @@ from django.views.generic import TemplateView from django.conf.urls import patterns, url -from .views import Register, VerifyEmail +from .views import RegisterView, VerifyEmailView urlpatterns = patterns( '', - url(r'^$', Register.as_view(), name='rest_register'), - url(r'^verify-email/$', VerifyEmail.as_view(), name='rest_verify_email'), + url(r'^$', RegisterView.as_view(), name='rest_register'), + url(r'^verify-email/$', VerifyEmailView.as_view(), name='rest_verify_email'), # This url is used by django-allauth and empty TemplateView is # defined just to allow reverse() call inside app, for example when email diff --git a/rest_auth/registration/views.py b/rest_auth/registration/views.py index 55852dd..2fa84bf 100644 --- a/rest_auth/registration/views.py +++ b/rest_auth/registration/views.py @@ -11,10 +11,10 @@ from allauth.account import app_settings from rest_auth.app_settings import UserDetailsSerializer, TokenSerializer from rest_auth.registration.serializers import SocialLoginSerializer -from rest_auth.views import Login +from rest_auth.views import LoginView -class Register(APIView, SignupView): +class RegisterView(APIView, SignupView): """ Accepts the credentials and creates a new user if user does not exist already @@ -69,7 +69,7 @@ class Register(APIView, SignupView): return Response(self.form.errors, status=status.HTTP_400_BAD_REQUEST) -class VerifyEmail(APIView, ConfirmEmailView): +class VerifyEmailView(APIView, ConfirmEmailView): permission_classes = (AllowAny,) allowed_methods = ('POST', 'OPTIONS', 'HEAD') @@ -84,14 +84,14 @@ class VerifyEmail(APIView, ConfirmEmailView): return Response({'message': 'ok'}, status=status.HTTP_200_OK) -class SocialLogin(Login): +class SocialLoginView(LoginView): """ class used for social authentications example usage for facebook with access_token ------------- from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter - class FacebookLogin(SocialLogin): + class FacebookLogin(SocialLoginView): adapter_class = FacebookOAuth2Adapter ------------- @@ -101,7 +101,7 @@ class SocialLogin(Login): from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Client - class FacebookLogin(SocialLogin): + class FacebookLogin(SocialLoginView): adapter_class = FacebookOAuth2Adapter client_class = OAuth2Client callback_url = 'localhost:8000' diff --git a/rest_auth/test_urls.py b/rest_auth/test_urls.py index e714598..ae5ef1d 100644 --- a/rest_auth/test_urls.py +++ b/rest_auth/test_urls.py @@ -5,10 +5,10 @@ import rest_auth.django_test_urls from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter from rest_auth.urls import urlpatterns -from rest_auth.registration.views import SocialLogin +from rest_auth.registration.views import SocialLoginView -class FacebookLogin(SocialLogin): +class FacebookLogin(SocialLoginView): adapter_class = FacebookOAuth2Adapter urlpatterns += patterns( diff --git a/rest_auth/urls.py b/rest_auth/urls.py index be14703..d753c44 100644 --- a/rest_auth/urls.py +++ b/rest_auth/urls.py @@ -1,21 +1,21 @@ from django.conf.urls import patterns, url from rest_auth.views import ( - Login, Logout, UserDetails, PasswordChange, - PasswordReset, PasswordResetConfirm + LoginView, LogoutView, UserDetailsView, PasswordChangeView, + PasswordResetView, PasswordResetConfirmView ) urlpatterns = patterns( '', # URLs that do not require a session or valid token - url(r'^password/reset/$', PasswordReset.as_view(), + url(r'^password/reset/$', PasswordResetView.as_view(), name='rest_password_reset'), - url(r'^password/reset/confirm/$', PasswordResetConfirm.as_view(), + url(r'^password/reset/confirm/$', PasswordResetConfirmView.as_view(), name='rest_password_reset_confirm'), - url(r'^login/$', Login.as_view(), name='rest_login'), + url(r'^login/$', LoginView.as_view(), name='rest_login'), # URLs that require a user to be logged in with a valid session / token. - url(r'^logout/$', Logout.as_view(), name='rest_logout'), - url(r'^user/$', UserDetails.as_view(), name='rest_user_details'), - url(r'^password/change/$', PasswordChange.as_view(), + url(r'^logout/$', LogoutView.as_view(), name='rest_logout'), + 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/views.py b/rest_auth/views.py index ab06528..c87a7e7 100644 --- a/rest_auth/views.py +++ b/rest_auth/views.py @@ -16,7 +16,7 @@ from .app_settings import ( ) -class Login(GenericAPIView): +class LoginView(GenericAPIView): """ Check the credentials and return the REST Token @@ -57,7 +57,7 @@ class Login(GenericAPIView): return self.get_response() -class Logout(APIView): +class LogoutView(APIView): """ Calls Django logout method and delete the Token object @@ -79,7 +79,7 @@ class Logout(APIView): status=status.HTTP_200_OK) -class UserDetails(RetrieveUpdateAPIView): +class UserDetailsView(RetrieveUpdateAPIView): """ Returns User's details in JSON format. @@ -97,7 +97,7 @@ class UserDetails(RetrieveUpdateAPIView): return self.request.user -class PasswordReset(GenericAPIView): +class PasswordResetView(GenericAPIView): """ Calls Django Auth PasswordResetForm save method. @@ -124,7 +124,7 @@ class PasswordReset(GenericAPIView): ) -class PasswordResetConfirm(GenericAPIView): +class PasswordResetConfirmView(GenericAPIView): """ Password reset e-mail link is confirmed, therefore this resets the user's password. @@ -147,7 +147,7 @@ class PasswordResetConfirm(GenericAPIView): return Response({"success": "Password has been reset with the new password."}) -class PasswordChange(GenericAPIView): +class PasswordChangeView(GenericAPIView): """ Calls Django Auth SetPasswordForm save method. From 02bf6fbe5bb489acdd43bf64dbb922f21d441a1a Mon Sep 17 00:00:00 2001 From: Philippe Luickx Date: Fri, 7 Aug 2015 14:31:33 +0300 Subject: [PATCH 08/11] bugfix, request.DATA is deprecated, replaced with request.data --- rest_auth/registration/views.py | 4 ++-- rest_auth/views.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/rest_auth/registration/views.py b/rest_auth/registration/views.py index 2fa84bf..8521981 100644 --- a/rest_auth/registration/views.py +++ b/rest_auth/registration/views.py @@ -51,7 +51,7 @@ class RegisterView(APIView, SignupView): def post(self, request, *args, **kwargs): self.initial = {} - self.request.POST = self.request.DATA.copy() + 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(): @@ -78,7 +78,7 @@ class VerifyEmailView(APIView, ConfirmEmailView): return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED) def post(self, request, *args, **kwargs): - self.kwargs['key'] = self.request.DATA.get('key', '') + self.kwargs['key'] = self.request.data.get('key', '') confirmation = self.get_object() confirmation.confirm(self.request) return Response({'message': 'ok'}, status=status.HTTP_200_OK) diff --git a/rest_auth/views.py b/rest_auth/views.py index c87a7e7..d789ac4 100644 --- a/rest_auth/views.py +++ b/rest_auth/views.py @@ -50,7 +50,7 @@ class LoginView(GenericAPIView): ) def post(self, request, *args, **kwargs): - self.serializer = self.get_serializer(data=self.request.DATA) + self.serializer = self.get_serializer(data=self.request.data) if not self.serializer.is_valid(): return self.get_error_response() self.login() @@ -110,8 +110,8 @@ class PasswordResetView(GenericAPIView): permission_classes = (AllowAny,) def post(self, request, *args, **kwargs): - # Create a serializer with request.DATA - serializer = self.get_serializer(data=request.DATA) + # Create a serializer with request.data + serializer = self.get_serializer(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, @@ -138,7 +138,7 @@ class PasswordResetConfirmView(GenericAPIView): permission_classes = (AllowAny,) def post(self, request): - serializer = self.get_serializer(data=request.DATA) + serializer = self.get_serializer(data=request.data) if not serializer.is_valid(): return Response( serializer.errors, status=status.HTTP_400_BAD_REQUEST @@ -160,7 +160,7 @@ class PasswordChangeView(GenericAPIView): permission_classes = (IsAuthenticated,) def post(self, request): - serializer = self.get_serializer(data=request.DATA) + serializer = self.get_serializer(data=request.data) if not serializer.is_valid(): return Response( serializer.errors, status=status.HTTP_400_BAD_REQUEST From 5a6c8f549be6a97b6a43281fba7852240d4f6bfa Mon Sep 17 00:00:00 2001 From: Philippe Luickx Date: Mon, 10 Aug 2015 11:24:21 +0300 Subject: [PATCH 09/11] bugfix --- rest_auth/registration/serializers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rest_auth/registration/serializers.py b/rest_auth/registration/serializers.py index d77eaac..2500e32 100644 --- a/rest_auth/registration/serializers.py +++ b/rest_auth/registration/serializers.py @@ -72,14 +72,14 @@ class SocialLoginSerializer(serializers.Serializer): code = attrs.get('code') - provider = self.adapter.get_provider() + provider = adapter.get_provider() scope = provider.get_scope(request) client = self.client_class( request, app.client_id, app.secret, - self.adapter.access_token_method, - self.adapter.access_token_url, + adapter.access_token_method, + adapter.access_token_url, self.callback_url, scope ) From 64ab8be2f0c0b89f6c38f00e10c2136c6f4ef79f Mon Sep 17 00:00:00 2001 From: Philippe Luickx Date: Thu, 13 Aug 2015 10:56:25 +0300 Subject: [PATCH 10/11] catching incorrect input --- rest_auth/registration/serializers.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rest_auth/registration/serializers.py b/rest_auth/registration/serializers.py index 2500e32..3403c7c 100644 --- a/rest_auth/registration/serializers.py +++ b/rest_auth/registration/serializers.py @@ -85,6 +85,9 @@ class SocialLoginSerializer(serializers.Serializer): ) token = client.get_access_token(code) access_token = token['access_token'] + + else: + raise serializers.ValidationError('Incorrect input. access_token or code is required.') token = adapter.parse_token({'access_token': access_token}) token.app = app From 388314f831dcfb51b5922028101efda0ffb92861 Mon Sep 17 00:00:00 2001 From: Mateusz Sikora Date: Fri, 14 Aug 2015 13:49:47 +0200 Subject: [PATCH 11/11] fix flake8 --- rest_auth/registration/serializers.py | 2 +- rest_auth/registration/views.py | 2 +- rest_auth/serializers.py | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/rest_auth/registration/serializers.py b/rest_auth/registration/serializers.py index 3403c7c..f5c444e 100644 --- a/rest_auth/registration/serializers.py +++ b/rest_auth/registration/serializers.py @@ -85,7 +85,7 @@ class SocialLoginSerializer(serializers.Serializer): ) token = client.get_access_token(code) access_token = token['access_token'] - + else: raise serializers.ValidationError('Incorrect input. access_token or code is required.') diff --git a/rest_auth/registration/views.py b/rest_auth/registration/views.py index 8521981..1895267 100644 --- a/rest_auth/registration/views.py +++ b/rest_auth/registration/views.py @@ -9,7 +9,7 @@ from allauth.account.views import SignupView, ConfirmEmailView from allauth.account.utils import complete_signup from allauth.account import app_settings -from rest_auth.app_settings import UserDetailsSerializer, TokenSerializer +from rest_auth.app_settings import TokenSerializer from rest_auth.registration.serializers import SocialLoginSerializer from rest_auth.views import LoginView diff --git a/rest_auth/serializers.py b/rest_auth/serializers.py index 53fbcbc..9cfb6b9 100644 --- a/rest_auth/serializers.py +++ b/rest_auth/serializers.py @@ -11,7 +11,6 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers, exceptions from rest_framework.authtoken.models import Token -from rest_framework.authtoken.serializers import AuthTokenSerializer from rest_framework.exceptions import ValidationError