Completed
Push — master ( a1e949...06a497 )
by Andrew
26s
created

test()   A

Complexity

Conditions 1

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 2
rs 10
1
# -*- encoding: utf-8 -*-
2
import datetime
3
import json
4
import logging
5
6
import os
7
from django.conf import settings
8
from django.contrib.auth import authenticate
9
from django.contrib.auth import login as djangologin
10
from django.contrib.auth import logout as djangologout
11
12
from chat.templatetags.md5url import md5url
13
14
try:
15
	from django.template.context_processors import csrf
16
except ImportError:
17
	from django.core.context_processors import csrf
18
from django.core.exceptions import ObjectDoesNotExist, ValidationError
19
from django.db import transaction
20
from django.db.models import Count, Q, F
21
from django.http import Http404
22
from django.utils.timezone import utc
23
from django.http import HttpResponse
24
from django.http import HttpResponseRedirect
25
from django.shortcuts import render_to_response
26
from django.template import RequestContext
27
from django.views.decorators.http import require_http_methods
28
from django.views.generic import View
29
from chat import utils
30
from chat.decorators import login_required_no_redirect
31
from chat.forms import UserProfileForm, UserProfileReadOnlyForm
32
from chat.models import Issue, IssueDetails, IpAddress, UserProfile, Verification, Message, Subscription
33
from django.conf import settings
34
from chat.utils import hide_fields, check_user, check_password, check_email, extract_photo, send_sign_up_email, \
35
	create_user_model, check_captcha, send_reset_password_email
36
37
logger = logging.getLogger(__name__)
38
RECAPTCHA_SITE_KEY = getattr(settings, "RECAPTCHA_SITE_KEY", None)
39
RECAPTHCA_SITE_URL = getattr(settings, "RECAPTHCA_SITE_URL", None)
40
GOOGLE_OAUTH_2_CLIENT_ID = getattr(settings, "GOOGLE_OAUTH_2_CLIENT_ID", None)
41
GOOGLE_OAUTH_2_JS_URL = getattr(settings, "GOOGLE_OAUTH_2_JS_URL", None)
42
FACEBOOK_APP_ID = getattr(settings, "FACEBOOK_APP_ID", None)
43
FACEBOOK_JS_URL = getattr(settings, "FACEBOOK_JS_URL", None)
44
FIREBASE_API_KEY = getattr(settings, "FIREBASE_API_KEY", None)
45
46
# TODO doesn't work
47
def handler404(request):
48
	return HttpResponse("Page not found", content_type='text/plain')
49
50
51
@require_http_methods(['POST'])
52
def validate_email(request):
53
	"""
54
	POST only, validates email during registration
55
	"""
56
	email = request.POST.get('email')
57
	try:
58
		utils.check_email(email)
59
		response = settings.VALIDATION_IS_OK
60
	except ValidationError as e:
61
		response = e.message
62
	return HttpResponse(response, content_type='text/plain')
63
64
65
@require_http_methods('GET')
66
def get_firebase_playback(request):
67
	registration_id = request.META['HTTP_AUTH']
68
	user_id = Subscription.objects.values_list('user_id', flat=True).get(registration_id=registration_id)
69
	off_mess = Message.objects.filter(
70
		id__gt=F('room__roomusers__last_read_message_id'),
71
		deleted=False,
72
		room__roomusers__user_id=user_id
73
	)
74
	d = off_mess.select_related("sender__username").order_by("-time")[:1]
75
	if len(d) > 0:
76
		message = list(d)[0]
77
		data = {
78
			'title': message.sender.username,
79
			'options': {
80
				'body': message.content,
81
				'icon': md5url('images/favicon.ico'),
82
				'data': {
83
					'url': '/#/chat/' + str(message.room_id)
84
				}
85
			},
86
		}
87
		return HttpResponse(json.dumps(data), content_type='application/json')
88
	else:
89
		return HttpResponse("No new messages found", content_type='text/plain')
90
91
92
def test(request):
93
	return HttpResponse(settings.VALIDATION_IS_OK, content_type='text/plain')
94
95
96
@require_http_methods('POST')
97
def register_subscription(request):
98
	registration_id = request.POST['registration_id']
99
	Subscription.objects.update_or_create(registration_id=registration_id, defaults={ 'user': request.user})
100
	return HttpResponse(settings.VALIDATION_IS_OK, content_type='text/plain')
101
102
@require_http_methods('POST')
103
def validate_user(request):
104
	"""
105
	Validates user during registration
106
	"""
107
	try:
108
		username = request.POST.get('username')
109
		utils.check_user(username)
110
		# hardcoded ok check in register.js
111
		message = settings.VALIDATION_IS_OK
112
	except ValidationError as e:
113
		message = e.message
114
	return HttpResponse(message, content_type='text/plain')
115
116
117
def get_service_worker(request):  # this stub is only for development, this is replaced in nginx for prod
118
	worker = open(os.path.join(settings.STATIC_ROOT, 'js', 'sw.js'), 'rb')
119
	response = HttpResponse(content=worker)
120
	response['Content-Type'] = 'application/javascript'
121
	return response
122
123
@require_http_methods('GET')
124
@login_required_no_redirect(False)
125
def home(request):
126
	"""
127
	Login or logout navbar is creates by means of create_nav_page
128
	@return:  the x intercept of the line M{y=m*x+b}.
129
	"""
130
	context = csrf(request)
131
	up = UserProfile.objects.defer('suggestions', 'notifications', 'cache_messages', 'highlight_code', 'embedded_youtube').get(id=request.user.id)
132
	context['suggestions'] = up.suggestions
133
	context['notifications'] = up.notifications
134
	context['cache_messages'] = up.cache_messages
135
	context['highlight_code'] = up.highlight_code
136
	context['embedded_youtube'] = up.embedded_youtube
137
	context['extensionId'] = settings.EXTENSION_ID
138
	context['extensionUrl'] = settings.EXTENSION_INSTALL_URL
139
	context['defaultRoomId'] = settings.ALL_ROOM_ID
140
	context['manifest'] = FIREBASE_API_KEY is not None
141
	return render_to_response('chat.html', context, context_instance=RequestContext(request))
142
143
144
@login_required_no_redirect(True)
145
def logout(request):
146
	"""
147
	POST. Logs out into system.
148
	"""
149
	djangologout(request)
150
	return HttpResponseRedirect('/')
151
152
@require_http_methods(['POST'])
153
def auth(request):
154
	"""
155
	Logs in into system.
156
	"""
157
	username = request.POST.get('username')
158
	password = request.POST.get('password')
159
	user = authenticate(username=username, password=password)
160
	if user is not None:
161
		djangologin(request, user)
162
		message = settings.VALIDATION_IS_OK
163
	else:
164
		message = 'Login or password is wrong'
165
	logger.debug('Auth request %s ; Response: %s', hide_fields(request.POST, ('password',)), message)
166
	return HttpResponse(message, content_type='text/plain')
167
168
169
def send_restore_password(request):
170
	"""
171
	Sends email verification code
172
	"""
173
	logger.debug('Recover password request %s', request)
174
	try:
175
		username_or_password = request.POST.get('username_or_password')
176
		check_captcha(request)
177
		user_profile = UserProfile.objects.get(Q(username=username_or_password) | Q(email=username_or_password))
178
		if not user_profile.email:
179
			raise ValidationError("You didn't specify email address for this user")
180
		verification = Verification(type_enum=Verification.TypeChoices.password, user_id=user_profile.id)
181
		verification.save()
182
		send_reset_password_email(request, user_profile, verification)
183
		message = settings.VALIDATION_IS_OK
184
		logger.debug('Verification email has been send for token %s to user %s(id=%d)',
185
				verification.token, user_profile.username, user_profile.id)
186
	except UserProfile.DoesNotExist:
187
		message = "User with this email or username doesn't exist"
188
		logger.debug("Skipping password recovery request for nonexisting user")
189
	except (UserProfile.DoesNotExist, ValidationError) as e:
190
		logger.debug('Not sending verification email because %s', e)
191
		message = 'Unfortunately we were not able to send you restore password email because {}'.format(e)
192
	return HttpResponse(message, content_type='text/plain')
193
194
195
def get_html_restore_pass():
196
	""""""
197
198
class RestorePassword(View):
199
200
	def get_user_by_code(self, token):
201
		"""
202
		:param token: token code to verify
203
		:type token: str
204
		:raises ValidationError: if token is not usable
205
		:return: UserProfile, Verification: if token is usable
206
		"""
207
		try:
208
			v = Verification.objects.get(token=token)
209
			if v.type_enum != Verification.TypeChoices.password:
210
				raise ValidationError("it's not for this operation ")
211
			if v.verified:
212
				raise ValidationError("it's already used")
213
			# TODO move to sql query or leave here?
214
			if v.time < datetime.datetime.utcnow().replace(tzinfo=utc) - datetime.timedelta(days=1):
215
				raise ValidationError("it's expired")
216
			return UserProfile.objects.get(id=v.user_id), v
217
		except Verification.DoesNotExist:
218
			raise ValidationError('Unknown verification token')
219
220
	@transaction.atomic
221
	def post(self, request):
222
		"""
223
		Sends email verification token
224
		"""
225
		token = request.POST.get('token', False)
226
		try:
227
			logger.debug('Proceed Recover password with token %s', token)
228
			user, verification = self.get_user_by_code(token)
229
			password = request.POST.get('password')
230
			check_password(password)
231
			user.set_password(password)
232
			user.save(update_fields=('password',))
233
			verification.verified = True
234
			verification.save(update_fields=('verified',))
235
			logger.info('Password has been change for token %s user %s(id=%d)', token, user.username, user.id)
236
			response = settings.VALIDATION_IS_OK
237
		except ValidationError as e:
238
			logger.debug('Rejecting verification token %s because %s', token, e)
239
			response = "".join(("You can't reset password with this token because ", str(e)))
240
		return HttpResponse(response, content_type='text/plain')
241
242
	def get(self, request):
243
		token = request.GET.get('token', False)
244
		logger.debug('Rendering restore password page with token  %s', token)
245
		try:
246
			user = self.get_user_by_code(token)[0]
247
			response = {
248
				'message': settings.VALIDATION_IS_OK,
249
				'restore_user': user.username,
250
				'token': token
251
			}
252
		except ValidationError as e:
253
			logger.debug('Rejecting verification token %s because %s', token, e)
254
			response = {'message': "Unable to confirm email with token {} because {}".format(token, e)}
255
		return render_to_response('reset_password.html', response, context_instance=RequestContext(request))
256
257
258
@require_http_methods('GET')
259
def confirm_email(request):
260
	"""
261
	Accept the verification token sent to email
262
	"""
263
	token = request.GET.get('token', False)
264
	logger.debug('Processing email confirm with token  %s', token)
265
	try:
266
		try:
267
			v = Verification.objects.get(token=token)
268
		except Verification.DoesNotExist:
269
			raise ValidationError('Unknown verification token')
270
		if v.type_enum != Verification.TypeChoices.register:
271
			raise ValidationError('This is not confirm email token')
272
		if v.verified:
273
			raise ValidationError('This verification token already accepted')
274
		user = UserProfile.objects.get(id=v.user_id)
275
		if user.email_verification_id != v.id:
276
			raise ValidationError('Verification token expired because you generated another one')
277
		v.verified = True
278
		v.save(update_fields=['verified'])
279
		message = settings.VALIDATION_IS_OK
280
		logger.info('Email verification token %s has been accepted for user %s(id=%d)', token, user.username, user.id)
281
	except Exception as e:
282
		logger.debug('Rejecting verification token %s because %s', token, e)
283
		message = ("Unable to confirm email with token {} because {}".format(token, e))
284
	response = {'message': message}
285
	return render_to_response('confirm_mail.html', response, context_instance=RequestContext(request))
286
287
288
@require_http_methods('GET')
289
def show_profile(request, profile_id):
290
	try:
291
		user_profile = UserProfile.objects.get(pk=profile_id)
292
		form = UserProfileReadOnlyForm(instance=user_profile)
293
		form.username = user_profile.username
294
		return render_to_response(
295
			'show_profile.html',
296
			{'form': form},
297
			context_instance=RequestContext(request)
298
		)
299
	except ObjectDoesNotExist:
300
		raise Http404
301
302
303
@require_http_methods('GET')
304
def statistics(request):
305
	pie_data = IpAddress.objects.values('country').filter(country__isnull=False).annotate(count=Count("country"))
306
	return HttpResponse(json.dumps(list(pie_data)), content_type='application/json')
307
308
309
@login_required_no_redirect()
310
@transaction.atomic
311
def report_issue(request):
312
	logger.info('Saving issue: %s', hide_fields(request.POST, ('log',), huge=True))
313
	issue = Issue.objects.get_or_create(content=request.POST['issue'])[0]
314
	issue_details = IssueDetails(
315
		sender_id=request.user.id,
316
		browser=request.POST.get('browser'),
317
		issue=issue,
318
		log=request.POST.get('log')
319
	)
320
	issue_details.save()
321
	return HttpResponse(settings.VALIDATION_IS_OK, content_type='text/plain')
322
323
324
class ProfileView(View):
325
326
	@login_required_no_redirect()
327
	def get(self, request):
328
		user_profile = UserProfile.objects.get(pk=request.user.id)
329
		form = UserProfileForm(instance=user_profile)
330
		c = csrf(request)
331
		c['form'] = form
332
		c['date_format'] = settings.DATE_INPUT_FORMATS_JS
333
		return render_to_response('change_profile.html', c, context_instance=RequestContext(request))
334
335
	@login_required_no_redirect()
336
	def post(self, request):
337
		logger.info('Saving profile: %s', hide_fields(request.POST, ("base64_image", ), huge=True))
338
		user_profile = UserProfile.objects.get(pk=request.user.id)
339
		image_base64 = request.POST.get('base64_image')
340
341
		if image_base64 is not None:
342
			image = extract_photo(image_base64)
343
			request.FILES['photo'] = image
344
345
		form = UserProfileForm(request.POST, request.FILES, instance=user_profile)
346
		if form.is_valid():
347
			profile = form.save()
348
			response = profile. photo.url if 'photo' in  request.FILES else settings.VALIDATION_IS_OK
349
		else:
350
			response = form.errors
351
		return HttpResponse(response, content_type='text/plain')
352
353
354
class RegisterView(View):
355
356
	def get(self, request):
357
		logger.debug(
358
			'Rendering register page with captcha site key %s and oauth key %s',
359
			RECAPTCHA_SITE_KEY, GOOGLE_OAUTH_2_CLIENT_ID
360
		)
361
		c = csrf(request)
362
		c['captcha_key'] = RECAPTCHA_SITE_KEY
363
		c['captcha_url'] = RECAPTHCA_SITE_URL
364
		c['oauth_url'] = GOOGLE_OAUTH_2_JS_URL
365
		c['oauth_token'] = GOOGLE_OAUTH_2_CLIENT_ID
366
		c['fb_app_id'] = FACEBOOK_APP_ID
367
		c['fb_js_url'] = FACEBOOK_JS_URL
368
		return render_to_response("register.html", c, context_instance=RequestContext(request))
369
370
	@transaction.atomic
371
	def post(self, request):
372
		try:
373
			rp = request.POST
374
			logger.info('Got register request %s', hide_fields(rp, ('password', 'repeatpassword')))
375
			(username, password, email) = (rp.get('username'), rp.get('password'), rp.get('email'))
376
			check_user(username)
377
			check_password(password)
378
			check_email(email)
379
			user_profile = UserProfile(username=username, email=email, sex_str=rp.get('sex'))
380
			user_profile.set_password(password)
381
			create_user_model(user_profile)
382
			# You must call authenticate before you can call login
383
			auth_user = authenticate(username=username, password=password)
384
			message = settings.VALIDATION_IS_OK  # redirect
385
			if email:
386
				send_sign_up_email(user_profile, request.get_host(), request)
387
			djangologin(request, auth_user)
388
		except ValidationError as e:
389
			message = e.message
390
			logger.debug('Rejecting request because "%s"', message)
391
		return HttpResponse(message, content_type='text/plain')
392