GoogleAuth.generate_user_profile()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 7
rs 10
1
import logging
2
import re
3
4
import requests
5
from django.contrib.auth import login as djangologin
6
from django.core.exceptions import ValidationError
7
from django.core.files.base import ContentFile
8
from django.http import HttpResponse
9
from django.views.generic import View
10
from oauth2client import client
11
from oauth2client.crypt import AppIdentityError
12
13
from chat import settings
14
from chat.log_filters import id_generator
15
from chat.models import UserProfile, get_random_path
16
from chat.py2_3 import urlopen
17
from chat.settings import VALIDATION_IS_OK, AUTHENTICATION_BACKENDS
18
from chat.utils import create_user_model, check_user
19
20
GOOGLE_OAUTH_2_CLIENT_ID = getattr(settings, "GOOGLE_OAUTH_2_CLIENT_ID", None)
21
GOOGLE_OAUTH_2_HOST = getattr(settings, "GOOGLE_OAUTH_2_HOST", None)
22
FACEBOOK_ACCESS_TOKEN = getattr(settings, "FACEBOOK_ACCESS_TOKEN", None)
23
24
logger = logging.getLogger(__name__)
25
26
27
class SocialAuth(View):
28
29
	@property
30
	def app_token(self):
31
		raise NotImplemented
32
33
	@property
34
	def instance(self):
35
		raise NotImplemented
36
37
	def generate_user_profile(self, token):
38
		raise NotImplemented
39
40
	def download_http_photo(self, url, user_profile):
41
		if url is not None:
42
			try:
43
				response = urlopen(url)
44
				filename = get_random_path(None, url.split('/')[-1])
45
				content = ContentFile(response.read())
46
				user_profile.photo.save(filename, content, False)
47
			except Exception as e:
48
				logger.error("Unable to download photo from url %s for user %s because %s",
49
						url, user_profile.username, e)
50
51
	def create_user_profile(self, email, name, surname, picture):
52
		try:
53
			user_profile = UserProfile.objects.get(email=email)
54
			logger.info("Sign in as %s with id %s", user_profile.username, user_profile.id)
55
		except UserProfile.DoesNotExist:
56
			try:
57
				logger.info(
58
					"Creating new user with email %s, name %s, surname %s, picture %s",
59
					email, name, surname, picture
60
				)
61
				# replace all characters but a valid one with '-' and cut to 15 chars
62
				username = re.sub('[^0-9a-zA-Z-_]+', '-', email.rsplit('@')[0])[:15]
63
				check_user(username)
64
			except ValidationError as e:
65
				logger.info("Can't use username %s because %s", username, e)
66
				username = id_generator(8)
67
			logger.debug("Generated username: %s", username)
68
			user_profile = UserProfile(
69
				name=name,
70
				surname=surname,
71
				email=email,
72
				username=username
73
			)
74
			self.download_http_photo(picture, user_profile)
75
			user_profile.save()
76
			create_user_model(user_profile)
77
		return user_profile
78
79
	def post(self, request):
80
		try:
81
			rp = request.POST
82
			logger.info('Got %s request: %s', self.instance, rp)
83
			token = rp.get('token')
84
			user_profile = self.generate_user_profile(token)
85
			user_profile.backend = AUTHENTICATION_BACKENDS[0]
86
			djangologin(request, user_profile)
87
			return HttpResponse(content=VALIDATION_IS_OK, content_type='text/plain')
88
		except ValidationError as e:
89
			logger.warn("Unable to proceed %s sing in because %s", self.instance, e.message)
90
			return HttpResponse(content="Unable to sign in via {} because '{}'".format(self.instance, e.message), content_type='text/plain')
91
92
93
class GoogleAuth(SocialAuth):
94
95
	@property
96
	def app_token(self):
97
		if GOOGLE_OAUTH_2_CLIENT_ID is None:
98
			raise ValidationError("GOOGLE_OAUTH_2_CLIENT_ID is not specified")
99
		return GOOGLE_OAUTH_2_CLIENT_ID
100
101
	@property
102
	def instance(self):
103
		return 'google'
104
105
	def verify_google_token(self, token):
106
		try:
107
			response = client.verify_id_token(token, None)
108
			if response['aud'] != self.app_token:
109
				raise ValidationError("Unrecognized client.")
110
			if response['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
111
				raise ValidationError("Wrong issuer.")
112
			if GOOGLE_OAUTH_2_HOST is not None and response['hd'] != GOOGLE_OAUTH_2_HOST:
113
				raise ValidationError("Wrong hosted domain.")
114
			if response['email'] is None:
115
				raise ValidationError("Google didn't provide an email")
116
			return response
117
		except AppIdentityError as e:
118
			raise ValidationError(e)
119
120
	def generate_user_profile(self, token):
121
		response = self.verify_google_token(token)
122
		return self.create_user_profile(
123
			response['email'],
124
			response.get('given_name'),
125
			response.get('family_name'),
126
			response.get('picture')
127
		)
128
129
130
class FacebookAuth(SocialAuth):
131
132
	@property
133
	def instance(self):
134
		return 'facebook'
135
136
	PARAMS = {
137
		'access_token': FACEBOOK_ACCESS_TOKEN,
138
		'fields': ",".join(('email', 'first_name', 'last_name'))
139
	}
140
141
	def get_facebook_user_id(self, token):
142
		r = requests.get(
143
			'https://graph.facebook.com/debug_token',
144
			{'input_token': token, 'access_token': FACEBOOK_ACCESS_TOKEN}
145
		)
146
		response = r.json()
147
		data = response.get('data')
148
		if data is None:
149
			raise ValidationError("response data is missing")
150
		if not data['is_valid']:
151
			error = data.get('error')
152
			if error is not None:
153
				raise ValidationError("Unable to verify token because '{} (code {}')".format(error.get('message'), error.get('code')))
154
			else:
155
				raise ValidationError("Unexpected error while verifying token")
156
		user_id = data.get('user_id')
157
		if user_id is None:
158
			raise ValidationError("Unable to login because data[user_id] is absent")
159
		return data.get('user_id')
160
161
	def get_facebook_user(self, user_id):
162
		url = "https://graph.facebook.com/{}".format(user_id)
163
		user_info = requests.get(url, self.PARAMS).json()
164
		if user_info.get('email') is None:
165
			raise ValidationError("Email for this user not found")
166
		return user_info
167
168
	def generate_user_profile(self, token):
169
		if FACEBOOK_ACCESS_TOKEN is None:
170
			raise ValidationError("FACEBOOK_ACCESS_TOKEN is not specified")
171
		user_id = self.get_facebook_user_id(token)
172
		user_info = self.get_facebook_user(user_id)
173
		return self.create_user_profile(
174
			user_info['email'],
175
			user_info.get('first_name'),
176
			user_info.get('last_name'),
177
			None
178
		)