mirror of
				https://github.com/Tivix/django-rest-auth.git
				synced 2025-11-01 00:07:39 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			218 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			218 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from django.http import HttpRequest
 | |
| from django.utils.translation import ugettext_lazy as _
 | |
| from django.contrib.auth import get_user_model
 | |
| 
 | |
| 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
 | |
|     from allauth.socialaccount.helpers import complete_social_login
 | |
|     from allauth.socialaccount.models import SocialAccount
 | |
|     from allauth.socialaccount.providers.base import AuthProcess
 | |
| except ImportError:
 | |
|     raise ImportError("allauth needs to be added to INSTALLED_APPS.")
 | |
| 
 | |
| from rest_framework import serializers
 | |
| from requests.exceptions import HTTPError
 | |
| 
 | |
| 
 | |
| class SocialAccountSerializer(serializers.ModelSerializer):
 | |
|     """
 | |
|     serialize allauth SocialAccounts for use with a REST API
 | |
|     """
 | |
|     class Meta:
 | |
|         model = SocialAccount
 | |
|         fields = (
 | |
|             'id',
 | |
|             'provider',
 | |
|             'uid',
 | |
|             'last_login',
 | |
|             'date_joined',
 | |
|         )
 | |
| 
 | |
| 
 | |
| class SocialLoginSerializer(serializers.Serializer):
 | |
|     access_token = serializers.CharField(required=False, allow_blank=True)
 | |
|     code = serializers.CharField(required=False, allow_blank=True)
 | |
| 
 | |
|     def _get_request(self):
 | |
|         request = self.context.get('request')
 | |
|         if not isinstance(request, HttpRequest):
 | |
|             request = request._request
 | |
|         return request
 | |
| 
 | |
|     def get_social_login(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
 | |
|         :returns: A populated instance of the
 | |
|             `allauth.socialaccount.SocialLoginView` 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):
 | |
|         view = self.context.get('view')
 | |
|         request = self._get_request()
 | |
| 
 | |
|         if not view:
 | |
|             raise serializers.ValidationError(
 | |
|                 _("View is not defined, pass it as a context variable")
 | |
|             )
 | |
| 
 | |
|         adapter_class = getattr(view, 'adapter_class', None)
 | |
|         if not adapter_class:
 | |
|             raise serializers.ValidationError(_("Define adapter_class in view"))
 | |
| 
 | |
|         adapter = adapter_class(request)
 | |
|         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 attrs.get('access_token'):
 | |
|             access_token = attrs.get('access_token')
 | |
| 
 | |
|         # Case 2: We received the authorization code
 | |
|         elif attrs.get('code'):
 | |
|             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 = adapter.get_provider()
 | |
|             scope = provider.get_scope(request)
 | |
|             client = self.client_class(
 | |
|                 request,
 | |
|                 app.client_id,
 | |
|                 app.secret,
 | |
|                 adapter.access_token_method,
 | |
|                 adapter.access_token_url,
 | |
|                 self.callback_url,
 | |
|                 scope
 | |
|             )
 | |
|             token = client.get_access_token(code)
 | |
|             access_token = token['access_token']
 | |
| 
 | |
|         else:
 | |
|             raise serializers.ValidationError(
 | |
|                 _("Incorrect input. access_token or code is required."))
 | |
| 
 | |
|         social_token = adapter.parse_token({'access_token': access_token})
 | |
|         social_token.app = app
 | |
| 
 | |
|         try:
 | |
|             login = self.get_social_login(adapter, app, social_token, access_token)
 | |
|             complete_social_login(request, login)
 | |
|         except HTTPError:
 | |
|             raise serializers.ValidationError(_("Incorrect value"))
 | |
| 
 | |
|         if not login.is_existing:
 | |
|             # We have an account already signed up in a different flow
 | |
|             # with the same email address: raise an exception.
 | |
|             # This needs to be handled in the frontend. We can not just
 | |
|             # link up the accounts due to security constraints
 | |
|             if allauth_settings.UNIQUE_EMAIL:
 | |
|                 # Do we have an account already with this email address?
 | |
|                 account_exists = get_user_model().objects.filter(
 | |
|                     email=login.user.email,
 | |
|                 ).exists()
 | |
|                 if account_exists:
 | |
|                     raise serializers.ValidationError(
 | |
|                         _("User is already registered with this e-mail address.")
 | |
|                     )
 | |
| 
 | |
|             login.lookup()
 | |
|             login.save(request, connect=True)
 | |
| 
 | |
|         attrs['user'] = login.account.user
 | |
| 
 | |
|         return attrs
 | |
| 
 | |
| 
 | |
| class SocialConnectMixin(object):
 | |
|     def get_social_login(self, *args, **kwargs):
 | |
|         """
 | |
|         Set the social login process state to connect rather than login
 | |
|         Refer to the implementation of get_social_login in base class and to the
 | |
|         allauth.socialaccount.helpers module complete_social_login function.
 | |
|         """
 | |
|         social_login = super(SocialConnectMixin, self).get_social_login(*args, **kwargs)
 | |
|         social_login.state['process'] = AuthProcess.CONNECT
 | |
|         return social_login
 | |
| 
 | |
| 
 | |
| class SocialConnectSerializer(SocialConnectMixin, SocialLoginSerializer):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| 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(write_only=True)
 | |
|     password2 = serializers.CharField(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()
 |