summaryrefslogtreecommitdiff
path: root/postgresqleu/auth.py
blob: c3118d518d56c75e7f1693e2798bc87d353e9b85 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#
# Django module to support postgresql.org community authentication 2.0
#
# The main location for this module is the pgweb git repository hosted
# on git.postgresql.org - look there for updates.
#
# To integrate with django, you need the following:
# * Make sure the view "login" from this module is used for login
# * Map an url somwehere (typicall /auth_receive/) to the auth_receive
#   view.
# * In settings.py, set AUTHENTICATION_BACKENDS to point to the class
#   AuthBackend in this module.
# * (And of course, register for a crypto key with the main authentication
#   provider website)
# * If the application uses the django admin interface, the login screen
#   has to be replaced with something similar to login.html in this
#   directory (adjust urls, and name it admin/login.html in any template
#   directory that's processed before the default django.contrib.admin)
#

from django.http import HttpResponseRedirect
from django.contrib.auth.models import User
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import login as django_login
from django.contrib.auth import logout as django_logout
from django.conf import settings

import base64
import urlparse
from urllib import quote_plus
from Crypto.Cipher import AES
import time

class AuthBackend(ModelBackend):
	# We declare a fake backend that always fails direct authentication -
	# since we should never be using direct authentication in the first place!
	def authenticate(self, username=None, password=None):
		raise Exception("Direct authentication not supported")


####
# Two regular django views to interact with the login system
####

# Handle login requests by sending them off to the main site
def login(request):
	if request.GET.has_key('next'):
		return HttpResponseRedirect("%s?su=%s" % (
				settings.PGAUTH_REDIRECT,
				quote_plus(request.GET['next']),
				))
	else:
		return HttpResponseRedirect(settings.PGAUTH_REDIRECT)

# Handle logout requests by logging out of this site and then
# redirecting to log out from the main site as well.
def logout(request):
	if request.user.is_authenticated():
		django_logout(request)
	return HttpResponseRedirect("%slogout/" % settings.PGAUTH_REDIRECT)

# Receive an authentication response from the main website and try
# to log the user in.
def auth_receive(request):
	if request.GET.has_key('s') and request.GET['s'] == "logout":
		# This was a logout request
		return HttpResponseRedirect('/')

	if not request.GET.has_key('i'):
		raise Exception("Missing IV")
	if not request.GET.has_key('d'):
		raise Exception("Missing data!")

	# Set up an AES object and decrypt the data we received
	decryptor = AES.new(base64.b64decode(settings.PGAUTH_KEY),
						AES.MODE_CBC,
						base64.b64decode(str(request.GET['i']), "-_"))
	s = decryptor.decrypt(base64.b64decode(str(request.GET['d']), "-_")).rstrip(' ')

	# Now un-urlencode it
	try:
		data = urlparse.parse_qs(s, strict_parsing=True)
	except ValueError, e:
		raise Exception("Invalid encrypted data received.")

	# Check the timestamp in the authentication
	if (int(data['t'][0]) < time.time() - 10):
		raise Exception("Authentication token too old.")

	# Update the user record (if any)
	try:
		user = User.objects.get(username=data['u'][0])
		# User found, let's see if any important fields have changed
		changed = False
		if user.first_name != data['f'][0]:
			user.first_name = data['f'][0]
			changed = True
		if user.last_name != data['l'][0]:
			user.last_name = data['l'][0]
			changed = True
		if user.email != data['e'][0]:
			user.email = data['e'][0]
			changed= True
		if changed:
			user.save()
	except User.DoesNotExist, e:
		# User not found, create it!
		user = User(username=data['u'][0],
					first_name=data['f'][0],
					last_name=data['l'][0],
					email=data['e'][0],
					password='setbypluginnotasha1',
					)
		user.save()

	# Ok, we have a proper user record. Now tell django that
	# we're authenticated so it persists it in the session. Before
	# we do that, we have to annotate it with the backend information.
	user.backend = "%s.%s" % (AuthBackend.__module__, AuthBackend.__name__)
	django_login(request, user)

	# Finally, redirect the user
	if data.has_key('su'):
		return HttpResponseRedirect(data['su'][0])
	# No redirect specified, see if we have it in our settings
	if hasattr(settings, 'PGAUTH_REDIRECT_SUCCESS'):
		return HttpResponseRedirect(settings.PGAUTH_REDIRECT_SUCCESS)
	raise Exception("Authentication successful, but don't know where to redirect!")