Skip to content

Commit fed6b98

Browse files
committed
Refactor social connect views and serializers
# 347
1 parent 41ae498 commit fed6b98

File tree

3 files changed

+178
-192
lines changed

3 files changed

+178
-192
lines changed

rest_auth/registration/serializers.py

Lines changed: 35 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from django.http import HttpRequest
2-
from django.conf import settings
32
from django.utils.translation import ugettext_lazy as _
43
from django.contrib.auth import get_user_model
54

@@ -9,13 +8,31 @@
98
get_username_max_length)
109
from allauth.account.adapter import get_adapter
1110
from allauth.account.utils import setup_user_email
11+
from allauth.socialaccount.helpers import complete_social_login
12+
from allauth.socialaccount.models import SocialAccount
13+
from allauth.socialaccount.providers.base import AuthProcess
1214
except ImportError:
1315
raise ImportError("allauth needs to be added to INSTALLED_APPS.")
1416

1517
from rest_framework import serializers
1618
from requests.exceptions import HTTPError
1719

1820

21+
class SocialAccountSerializer(serializers.ModelSerializer):
22+
"""
23+
serialize allauth SocialAccounts for use with a REST API
24+
"""
25+
class Meta:
26+
model = SocialAccount
27+
fields = (
28+
'id',
29+
'provider',
30+
'uid',
31+
'last_login',
32+
'date_joined',
33+
)
34+
35+
1936
class SocialLoginSerializer(serializers.Serializer):
2037
access_token = serializers.CharField(required=False, allow_blank=True)
2138
code = serializers.CharField(required=False, allow_blank=True)
@@ -105,7 +122,7 @@ def validate(self, attrs):
105122
login = self.get_social_login(adapter, app, social_token, access_token)
106123
complete_social_login(request, login)
107124
except HTTPError:
108-
raise serializers.ValidationError(_('Incorrect value'))
125+
raise serializers.ValidationError(_("Incorrect value"))
109126

110127
if not login.is_existing:
111128
# We have an account already signed up in a different flow
@@ -130,6 +147,22 @@ def validate(self, attrs):
130147
return attrs
131148

132149

150+
class SocialConnectMixin(object):
151+
def get_social_login(self, *args, **kwargs):
152+
"""
153+
Set the social login process state to connect rather than login
154+
Refer to the implementation of get_social_login in base class and to the
155+
allauth.socialaccount.helpers module complete_social_login function.
156+
"""
157+
social_login = super(SocialConnectMixin, self).get_social_login(*args, **kwargs)
158+
social_login.state['process'] = AuthProcess.CONNECT
159+
return social_login
160+
161+
162+
class SocialConnectSerializer(SocialConnectMixin, SocialLoginSerializer):
163+
pass
164+
165+
133166
class RegisterSerializer(serializers.Serializer):
134167
username = serializers.CharField(
135168
max_length=get_username_max_length(),
@@ -182,44 +215,3 @@ def save(self, request):
182215

183216
class VerifyEmailSerializer(serializers.Serializer):
184217
key = serializers.CharField()
185-
186-
187-
# Import is needed only if we are using social login, in which
188-
# case the allauth.socialaccount will be declared
189-
if 'allauth.socialaccount' in settings.INSTALLED_APPS:
190-
from allauth.socialaccount.helpers import complete_social_login
191-
from allauth.socialaccount.models import SocialAccount
192-
from allauth.socialaccount.providers.base import AuthProcess
193-
194-
class SocialAccountSerializer(serializers.ModelSerializer):
195-
"""
196-
serialize allauth SocialAccounts for use with a REST API
197-
"""
198-
class Meta:
199-
model = SocialAccount
200-
fields = (
201-
'id',
202-
'provider',
203-
'uid',
204-
'last_login',
205-
'date_joined',
206-
'extra_data',
207-
)
208-
209-
210-
class SocialConnectMixin(object):
211-
def get_social_login(self, *args, **kwargs):
212-
"""
213-
set the social login process state to connect rather than login
214-
215-
Refer to the implementation of get_social_login in base class and to the
216-
allauth.socialaccount.helpers module complete_social_login function.
217-
"""
218-
219-
social_login = super(SocialConnectMixin, self).get_social_login(*args, **kwargs)
220-
social_login.state['process'] = AuthProcess.CONNECT
221-
return social_login
222-
223-
224-
class SocialConnectSerializer(SocialConnectMixin, SocialLoginSerializer):
225-
pass

rest_auth/registration/views.py

Lines changed: 75 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,25 @@
77
from rest_framework.response import Response
88
from rest_framework.permissions import (AllowAny,
99
IsAuthenticated)
10-
from rest_framework.decorators import detail_route
11-
from rest_framework.viewsets import GenericViewSet
12-
from rest_framework.generics import CreateAPIView
10+
from rest_framework.generics import CreateAPIView, ListAPIView, GenericAPIView
11+
from rest_framework.exceptions import NotFound
1312
from rest_framework import status
1413

14+
from allauth.account.adapter import get_adapter
1515
from allauth.account.views import ConfirmEmailView
1616
from allauth.account.utils import complete_signup
1717
from allauth.account import app_settings as allauth_settings
18+
from allauth.socialaccount import signals
19+
from allauth.socialaccount.adapter import get_adapter as get_social_adapter
20+
from allauth.socialaccount.models import SocialAccount
1821

1922
from rest_auth.app_settings import (TokenSerializer,
2023
JWTSerializer,
2124
create_token)
2225
from rest_auth.models import TokenModel
23-
from rest_auth.registration.serializers import (SocialLoginSerializer,
24-
VerifyEmailSerializer,
26+
from rest_auth.registration.serializers import (VerifyEmailSerializer,
27+
SocialLoginSerializer,
28+
SocialAccountSerializer,
2529
SocialConnectSerializer)
2630
from rest_auth.utils import jwt_encode
2731
from rest_auth.views import LoginView
@@ -94,98 +98,89 @@ def post(self, request, *args, **kwargs):
9498
return Response({'detail': _('ok')}, status=status.HTTP_200_OK)
9599

96100

97-
if 'allauth.socialaccount' in settings.INSTALLED_APPS:
98-
from allauth.socialaccount import signals
99-
from allauth.socialaccount.models import SocialAccount
100-
from allauth.socialaccount.adapter import get_adapter
101+
class SocialLoginView(LoginView):
102+
"""
103+
class used for social authentications
104+
example usage for facebook with access_token
105+
-------------
106+
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
101107
102-
from rest_auth.registration.serializers import SocialAccountSerializer
108+
class FacebookLogin(SocialLoginView):
109+
adapter_class = FacebookOAuth2Adapter
110+
-------------
103111
112+
example usage for facebook with code
104113
105-
class SocialLoginView(LoginView):
106-
"""
107-
class used for social authentications
108-
example usage for facebook with access_token
109-
-------------
110-
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
114+
-------------
115+
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
116+
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
111117
112-
class FacebookLogin(SocialLoginView):
113-
adapter_class = FacebookOAuth2Adapter
114-
-------------
118+
class FacebookLogin(SocialLoginView):
119+
adapter_class = FacebookOAuth2Adapter
120+
client_class = OAuth2Client
121+
callback_url = 'localhost:8000'
122+
-------------
123+
"""
124+
serializer_class = SocialLoginSerializer
115125

116-
example usage for facebook with code
126+
def process_login(self):
127+
get_adapter(self.request).login(self.request, self.user)
117128

118-
-------------
119-
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
120-
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
121129

122-
class FacebookLogin(SocialLoginView):
123-
adapter_class = FacebookOAuth2Adapter
124-
client_class = OAuth2Client
125-
callback_url = 'localhost:8000'
126-
-------------
127-
"""
130+
class SocialConnectView(LoginView):
131+
"""
132+
class used for social account linking
128133
129-
serializer_class = SocialLoginSerializer
134+
example usage for facebook with access_token
135+
-------------
136+
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
130137
131-
def process_login(self):
132-
get_adapter(self.request).login(self.request, self.user)
138+
class FacebookConnect(SocialConnectView):
139+
adapter_class = FacebookOAuth2Adapter
140+
-------------
141+
"""
142+
serializer_class = SocialConnectSerializer
143+
permission_classes = (IsAuthenticated,)
133144

145+
def process_login(self):
146+
get_adapter(self.request).login(self.request, self.user)
134147

135-
class SocialConnectView(SocialLoginView):
136-
"""
137-
class used for social account linking
138148

139-
example usage for facebook with access_token
140-
-------------
141-
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
149+
class SocialAccountListView(ListAPIView):
150+
"""
151+
List SocialAccounts for the currently logged in user
152+
"""
153+
serializer_class = SocialAccountSerializer
154+
permission_classes = (IsAuthenticated,)
142155

143-
class FacebookConnect(SocialConnectView):
144-
adapter_class = FacebookOAuth2Adapter
145-
-------------
146-
"""
156+
def get_queryset(self):
157+
return SocialAccount.objects.filter(user=self.request.user)
147158

148-
serializer_class = SocialConnectSerializer
149-
permission_classes = (IsAuthenticated,)
150159

160+
class SocialAccountDisconnectView(GenericAPIView):
161+
"""
162+
Disconnect SocialAccount from remote service for
163+
the currently logged in user
164+
"""
165+
serializer_class = SocialConnectSerializer
166+
permission_classes = (IsAuthenticated,)
151167

152-
class SocialAccountViewSet(GenericViewSet):
153-
"""
154-
allauth SocialAccount REST API read and disconnect views for logged in users
168+
def get_queryset(self):
169+
return SocialAccount.objects.filter(user=self.request.user)
155170

156-
Refer to the django-allauth package implementation of the models and
157-
account handling logic for more details, this viewset emulates the allauth web UI.
158-
"""
159-
160-
serializer_class = SocialAccountSerializer
161-
permission_classes = (IsAuthenticated,)
162-
queryset = SocialAccount.objects.none()
163-
164-
def get_queryset(self):
165-
return SocialAccount.objects.filter(user=self.request.user)
166-
167-
def list(self, request):
168-
"""
169-
list SocialAccounts for the currently logged in user
170-
"""
171-
172-
return Response(self.get_serializer(self.get_queryset(), many=True).data)
173-
174-
@detail_route(methods=['POST'])
175-
def disconnect(self, request, pk):
176-
"""
177-
disconnect SocialAccount from remote service for the currently logged in user
178-
"""
171+
def post(self, request, *args, **kwargs):
172+
accounts = self.get_queryset()
173+
account = accounts.filter(pk=kwargs['pk']).first()
174+
if not account:
175+
raise NotFound
179176

180-
accounts = self.get_queryset()
181-
account = accounts.get(pk=pk)
182-
get_adapter(self.request).validate_disconnect(account, accounts)
177+
get_social_adapter(self.request).validate_disconnect(account, accounts)
183178

184-
account.delete()
185-
signals.social_account_removed.send(
186-
sender=SocialAccount,
187-
request=self.request,
188-
socialaccount=account
189-
)
179+
account.delete()
180+
signals.social_account_removed.send(
181+
sender=SocialAccount,
182+
request=self.request,
183+
socialaccount=account
184+
)
190185

191-
return Response(self.get_serializer(account).data)
186+
return Response(self.get_serializer(account).data)

0 commit comments

Comments
 (0)