Completed
Push — master ( 752250...a6f716 )
by Andrew
01:11
created

update_session_key()   A

Complexity

Conditions 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 10
rs 9.4285
1
# -*- encoding: utf-8 -*-
2
import datetime
3
import json
4
5
from django.conf import settings
6
from django.contrib.auth import authenticate
7
from django.contrib.auth import login as djangologin
8
from django.contrib.auth import logout as djangologout
9
from django.core.mail import send_mail
10
11
try:
12
	from django.template.context_processors import csrf
13
except ImportError:
14
	from django.core.context_processors import csrf
15
from django.core.exceptions import ObjectDoesNotExist, ValidationError
16
from django.db import transaction
17
from django.db.models import Count, Q
18
from django.http import Http404
19
from django.http import HttpResponse
20
from django.http import HttpResponseRedirect
21
from django.shortcuts import render_to_response
22
from django.template import RequestContext
23
from django.views.decorators.http import require_http_methods
24
from django.views.generic import View
25
from chat import utils
26
from chat.decorators import login_required_no_redirect
27
from chat.forms import UserProfileForm, UserProfileReadOnlyForm
28
from chat.models import Issue, IssueDetails, IpAddress, UserProfile, Verification
29
from chat.settings import VALIDATION_IS_OK, DATE_INPUT_FORMATS_JS, logging, SITE_PROTOCOL
30
from chat.utils import hide_fields, check_user, check_password, check_email, extract_photo, send_email_verification, \
31
	create_user_profile, check_captcha
32
33
logger = logging.getLogger(__name__)
34
35
36
# TODO doesn't work
37
def handler404(request):
38
	return HttpResponse("Page not found", content_type='text/plain')
39
40
41
@require_http_methods(['POST'])
42
def validate_email(request):
43
	"""
44
	POST only, validates email during registration
45
	"""
46
	email = request.POST.get('email')
47
	try:
48
		utils.check_email(email)
49
		response = VALIDATION_IS_OK
50
	except ValidationError as e:
51
		response = e.message
52
	return HttpResponse(response, content_type='text/plain')
53
54
55
@require_http_methods('POST')
56
def validate_user(request):
57
	"""
58
	Validates user during registration
59
	"""
60
	try:
61
		username = request.POST.get('username')
62
		utils.check_user(username)
63
		# hardcoded ok check in register.js
64
		message = VALIDATION_IS_OK
65
	except ValidationError as e:
66
		message = e.message
67
	return HttpResponse(message, content_type='text/plain')
68
69
70
@require_http_methods('GET')
71
@login_required_no_redirect(False)
72
def home(request):
73
	"""
74
	Login or logout navbar is creates by means of create_nav_page
75
	@return:  the x intercept of the line M{y=m*x+b}.
76
	"""
77
	context = csrf(request)
78
	context['suggestions'] = UserProfile.objects.get(id=request.user.id).suggestions
79
	return render_to_response('chat.html', context, context_instance=RequestContext(request))
80
81
82
@login_required_no_redirect(True)
83
def logout(request):
84
	"""
85
	POST. Logs out into system.
86
	"""
87
	djangologout(request)
88
	response = HttpResponseRedirect('/')
89
	return response
90
91
92
@require_http_methods(['POST'])
93
def auth(request):
94
	"""
95
	Logs in into system.
96
	"""
97
	username = request.POST.get('username')
98
	password = request.POST.get('password')
99
	user = authenticate(username=username, password=password)
100
	if user is not None:
101
		djangologin(request, user)
102
		message = VALIDATION_IS_OK
103
	else:
104
		message = 'Login or password is wrong'
105
	logger.debug('Auth request %s ; Response: %s', hide_fields(request.POST, 'password'), message)
106
	response = HttpResponse(message, content_type='text/plain')
107
	return response
108
109
110
def send_restore_password(request):
111
	"""
112
	Sends email verification code
113
	"""
114
	logger.debug('Recover password request %s', request)
115
	try:
116
		username_or_password = request.POST.get('username_or_password')
117
		check_captcha(request)
118
		user_profile = UserProfile.objects.get(Q(username=username_or_password) | Q(email=username_or_password))
119
		if not user_profile.email:
120
			raise ValidationError("You didn't specify email address for this user")
121
		if not user_profile.email_verification_id or not user_profile.email_verification.verified:
122
			raise ValidationError("You didn't verify the email after registration. "
123
					"Find you verification email and complete verification and after restore the password again")
124
		verification = Verification(type_enum=Verification.TypeChoices.password, user_id=user_profile.id)
125
		verification.save()
126
		message = "{},\n" \
127
			"You requested to change a password on site {}.\n" \
128
			"To proceed click on the link {}://{}/restore_password?token={}\n" \
129
			"If you didn't request the password change just ignore this mail" \
130
			.format(user_profile.username, request.get_host(), SITE_PROTOCOL, request.get_host(), verification.token)
131
		send_mail("Change password", message, request.get_host(), (user_profile.email,), fail_silently=False)
132
		message = VALIDATION_IS_OK
133
		logger.debug('Verification email has been send for token %s to user %s(id=%d)',
134
				verification.token, user_profile.username, user_profile.id)
135
	except UserProfile.DoesNotExist:
136
		message = "User with this email or username doesn't exist"
137
		logger.debug("Skipping password recovery request for nonexisting user")
138
	except (UserProfile.DoesNotExist, ValidationError) as e:
139
		logger.debug('Not sending verification email because %s', e)
140
		message = 'Unfortunately we were not able to send you restore password email because {}'.format(e)
141
	return HttpResponse(message, content_type='text/plain')
142
143
144
class RestorePassword(View):
145
146
	def get_user_by_code(self, token):
147
		"""
148
		:param token: token code to verify
149
		:type token: str
150
		:raises ValidationError: if token is not usable
151
		:return: UserProfile, Verification: if token is usable
152
		"""
153
		try:
154
			v = Verification.objects.get(token=token)
155
			if v.type_enum != Verification.TypeChoices.password:
156
				raise ValidationError("it's not for this operation ")
157
			if v.verified:
158
				raise ValidationError("it's already used")
159
			# TODO move to sql query or leave here?
160
			if v.time < datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=1):
161
				raise ValidationError("it's expired")
162
			return UserProfile.objects.get(id=v.user_id), v
163
		except Verification.DoesNotExist:
164
			raise ValidationError('Unknown verification token')
165
166
	@transaction.atomic
167
	def post(self, request):
168
		"""
169
		Sends email verification token
170
		"""
171
		token = request.POST.get('token', False)
172
		try:
173
			logger.debug('Proceed Recover password with token %s', token)
174
			user, verification = self.get_user_by_code(token)
175
			password = request.POST.get('password')
176
			check_password(password)
177
			user.set_password(password)
178
			user.save(update_fields=('password',))
179
			verification.verified = True
180
			verification.save(update_fields=('verified',))
181
			logger.info('Password has been change for token %s user %s(id=%d)', token, user.username, user.id)
182
			response = VALIDATION_IS_OK
183
		except ValidationError as e:
184
			logger.debug('Rejecting verification token %s because %s', token, e)
185
			response = "".join(("You can't reset password with this token because ", str(e)))
186
		return HttpResponse(response, content_type='text/plain')
187
188
	def get(self, request):
189
		token = request.GET.get('token', False)
190
		logger.debug('Rendering restore password page with token  %s', token)
191
		try:
192
			user, verification = self.get_user_by_code(token)
193
			response = {
194
				'message': VALIDATION_IS_OK,
195
				'restore_user': user.username,
196
				'token': token
197
			}
198
		except ValidationError as e:
199
			logger.debug('Rejecting verification token %s because %s', token, e)
200
			response = {'message': "Unable to confirm email with token {} because {}".format(token, e)}
201
		return render_to_response('reset_password.html', response, context_instance=RequestContext(request))
202
203
204
@require_http_methods('GET')
205
def confirm_email(request):
206
	"""
207
	Accept the verification token sent to email
208
	"""
209
	token = request.GET.get('token', False)
210
	logger.debug('Processing email confirm with token  %s', token)
211
	try:
212
		try:
213
			v = Verification.objects.get(token=token)
214
		except Verification.DoesNotExist:
215
			raise ValidationError('Unknown verification token')
216
		if v.type_enum != Verification.TypeChoices.register:
217
			raise ValidationError('This is not confirm email token')
218
		if v.verified:
219
			raise ValidationError('This verification token already accepted')
220
		user = UserProfile.objects.get(id=v.user_id)
221
		if user.email_verification_id != v.id:
222
			raise ValidationError('Verification token expired because you generated another one')
223
		v.verified = True
224
		v.save(update_fields=['verified'])
225
		message = VALIDATION_IS_OK
226
		logger.info('Email verification token %s has been accepted for user %s(id=%d)', token, user.username, user.id)
227
	except Exception as e:
228
		logger.debug('Rejecting verification token %s because %s', token, e)
229
		message = ("Unable to confirm email with token {} because {}".format(token, e))
230
	response = {'message': message}
231
	return render_to_response('confirm_mail.html', response, context_instance=RequestContext(request))
232
233
234
@require_http_methods('GET')
235
def show_profile(request, profile_id):
236
	try:
237
		user_profile = UserProfile.objects.get(pk=profile_id)
238
		form = UserProfileReadOnlyForm(instance=user_profile)
239
		form.username = user_profile.username
240
		return render_to_response(
241
			'show_profile.html',
242
			{'form': form},
243
			context_instance=RequestContext(request)
244
		)
245
	except ObjectDoesNotExist:
246
		raise Http404
247
248
249
@require_http_methods('GET')
250
def statistics(request):
251
	pie_data = IpAddress.objects.values('country').filter(country__isnull=False).annotate(count=Count("country"))
252
	return HttpResponse(json.dumps(list(pie_data)), content_type='application/json')
253
254
255
@login_required_no_redirect()
256
@transaction.atomic
257
def report_issue(request):
258
	logger.info('Saving issue: %s', hide_fields(request.POST, 'log', huge=True))
259
	issue = Issue.objects.get_or_create(content=request.POST['issue'])[0]
260
	issue_details = IssueDetails(
261
		sender_id=request.user.id,
262
		browser=request.POST.get('browser'),
263
		issue=issue,
264
		log=request.POST.get('log')
265
	)
266
	issue_details.save()
267
	return HttpResponse(VALIDATION_IS_OK, content_type='text/plain')
268
269
270
class ProfileView(View):
271
272
	@login_required_no_redirect(True)
273
	def get(self, request):
274
		user_profile = UserProfile.objects.get(pk=request.user.id)
275
		form = UserProfileForm(instance=user_profile)
276
		c = csrf(request)
277
		c['form'] = form
278
		c['date_format'] = DATE_INPUT_FORMATS_JS
279
		return render_to_response('change_profile.html', c, context_instance=RequestContext(request))
280
281
	@login_required_no_redirect()
282
	def post(self, request):
283
		logger.info('Saving profile: %s', hide_fields(request.POST, "base64_image", huge=True))
284
		user_profile = UserProfile.objects.get(pk=request.user.id)
285
		image_base64 = request.POST.get('base64_image')
286
287
		if image_base64 is not None:
288
			image = extract_photo(image_base64)
289
			request.FILES['photo'] = image
290
291
		form = UserProfileForm(request.POST, request.FILES, instance=user_profile)
292
		if form.is_valid():
293
			profile = form.save()
294
			response = profile. photo.url if 'photo' in  request.FILES else VALIDATION_IS_OK
295
		else:
296
			response = form.errors
297
		return HttpResponse(response, content_type='text/plain')
298
299
300
class RegisterView(View):
301
302
	def get(self, request):
303
		c = csrf(request)
304
		c['captcha'] = getattr(settings, "RECAPTCHA_SITE_KEY", None)
305
		logger.debug('Rendering register page with captcha site key %s', c['captcha'])
306
		c['captcha_url'] = getattr(settings, "RECAPTHCA_SITE_URL", None)
307
		return render_to_response("register.html", c, context_instance=RequestContext(request))
308
309
	@transaction.atomic
310
	def post(self, request):
311
		try:
312
			rp = request.POST
313
			logger.info('Got register request %s', hide_fields(rp, 'password', 'repeatpassword'))
314
			(username, password, email) = (rp.get('username'), rp.get('password'), rp.get('email'))
315
			check_user(username)
316
			check_password(password)
317
			check_email(email)
318
			user_profile = create_user_profile(email, password, rp.get('sex'), username)
319
			# You must call authenticate before you can call login
320
			auth_user = authenticate(username=username, password=password)
321
			message = VALIDATION_IS_OK  # redirect
322
			if email:
323
				send_email_verification(user_profile, request.get_host())
324
			djangologin(request, auth_user)
325
		except ValidationError as e:
326
			message = e.message
327
			logger.debug('Rejecting request because "%s"', message)
328
		return HttpResponse(message, content_type='text/plain')
329