summaryrefslogtreecommitdiff
path: root/postgresqleu
diff options
context:
space:
mode:
Diffstat (limited to 'postgresqleu')
-rw-r--r--postgresqleu/accountinfo/views.py33
-rw-r--r--postgresqleu/auth.py62
-rw-r--r--postgresqleu/confsponsor/views.py20
-rw-r--r--postgresqleu/urls.py1
4 files changed, 113 insertions, 3 deletions
diff --git a/postgresqleu/accountinfo/views.py b/postgresqleu/accountinfo/views.py
index 72f22191..d4a6ba53 100644
--- a/postgresqleu/accountinfo/views.py
+++ b/postgresqleu/accountinfo/views.py
@@ -1,5 +1,6 @@
from django.http import HttpResponse
from django.db.models import Q
+from django.db import transaction
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
@@ -7,12 +8,14 @@ from django.contrib.auth.models import User
import simplejson as json
from postgresqleu.util.decorators import user_passes_test_or_error, ssl_required
+from postgresqleu.auth import user_search, user_import
@ssl_required
@login_required
@user_passes_test_or_error(lambda u: u.has_module_perms('invoices'))
def search(request):
term = request.GET['term']
+ upstream = request.GET.get('upstream', False)
users = User.objects.filter(
Q(username__icontains=term) |
@@ -20,4 +23,32 @@ def search(request):
Q(last_name__icontains=term) |
Q(email__icontains=term)
)
- return HttpResponse(json.dumps([{'ui': u.id, 'u': u.username, 'n': u.first_name + ' ' + u.last_name, 'e': u.email} for u in users]), content_type='application/json')
+ if users:
+ return HttpResponse(json.dumps([{'ui': u.id, 'u': u.username, 'n': u.first_name + ' ' + u.last_name, 'e': u.email} for u in users]), content_type='application/json')
+
+ if not upstream:
+ return HttpResponse('[]', content_type='application/json')
+
+ # Perform upstream search
+ users = user_search(term)
+ # All users need a negative id so we can differentiate them
+ for n in range(0, len(users)):
+ users[n]['i'] = -1-n
+ return HttpResponse(json.dumps([{'ui': u['i'],
+ 'u': u['u'],
+ 'n': u['f'] + ' ' + u['l'],
+ 'e': u['e'],
+ } for u in users]), content_type='application/json')
+
+@ssl_required
+@login_required
+@user_passes_test_or_error(lambda u: u.has_module_perms('invoices'))
+@transaction.commit_on_success
+def importuser(request):
+ uid = request.POST['uid']
+ try:
+ user_import(uid)
+ except Exception, e:
+ return HttpResponse('%s' % e, content_type='text/plain')
+
+ return HttpResponse('OK', content_type='text/plain')
diff --git a/postgresqleu/auth.py b/postgresqleu/auth.py
index ecb3d486..888aa9dd 100644
--- a/postgresqleu/auth.py
+++ b/postgresqleu/auth.py
@@ -6,7 +6,7 @@
#
# 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
+# * Map an url somwehere (typically /auth_receive/) to the auth_receive
# view.
# * In settings.py, set AUTHENTICATION_BACKENDS to point to the class
# AuthBackend in this module.
@@ -26,6 +26,8 @@ from django.contrib.auth import logout as django_logout
from django.conf import settings
import base64
+import simplejson
+import socket
import urlparse
import urllib
from Crypto.Cipher import AES
@@ -168,3 +170,61 @@ We apologize for the inconvenience.
if hasattr(settings, 'PGAUTH_REDIRECT_SUCCESS'):
return HttpResponseRedirect(settings.PGAUTH_REDIRECT_SUCCESS)
raise Exception("Authentication successful, but don't know where to redirect!")
+
+
+# Perform a search in the central system. Note that the results are returned as an
+# array of dicts, and *not* as User objects. To be able to for example reference the
+# user through a ForeignKey, a User object must be materialized locally. We don't do
+# that here, as this search might potentially return a lot of unrelated users since
+# it's a wildcard match.
+# Unlike the authentication, searching does not involve the browser - we just make
+# a direct http call.
+def user_search(searchterm=None, userid=None):
+ # If upsteam isn't responding quickly, it's not going to respond at all, and
+ # 10 seconds is already quite long.
+ socket.setdefaulttimeout(10)
+ if userid:
+ q = {'u': userid}
+ else:
+ q = {'s': searchterm}
+
+ u = urllib.urlopen('%ssearch/?%s' % (
+ settings.PGAUTH_REDIRECT,
+ urllib.urlencode(q),
+ ))
+ (ivs, datas) = u.read().split('&')
+ u.close()
+
+ # Decryption time
+ decryptor = AES.new(base64.b64decode(settings.PGAUTH_KEY),
+ AES.MODE_CBC,
+ base64.b64decode(ivs, "-_"))
+ s = decryptor.decrypt(base64.b64decode(datas, "-_")).rstrip(' ')
+ j = simplejson.loads(s)
+
+ return j
+
+# Import a user into the local authentication system. Will initially
+# make a search for it, and if anything other than one entry is returned
+# the import will fail.
+# Import is only supported based on userid - so a search should normally
+# be done first. This will result in multiple calls to the upstream
+# server, but they are cheap...
+# The call to this function should normally be wrapped in a transaction,
+# and this function itself will make no attempt to do anything about that.
+def user_import(uid):
+ u = user_search(userid=uid)
+ if len(u) != 1:
+ raise Exception("Internal error, duplicate or no user found")
+
+ u = u[0]
+
+ if User.objects.filter(username=u['u']).exists():
+ raise Exception("User already exists")
+
+ User(username=u['u'],
+ first_name=u['f'],
+ last_name=u['l'],
+ email=u['e'],
+ password='setbypluginnotsha1',
+ ).save()
diff --git a/postgresqleu/confsponsor/views.py b/postgresqleu/confsponsor/views.py
index b877b3a7..2464ca12 100644
--- a/postgresqleu/confsponsor/views.py
+++ b/postgresqleu/confsponsor/views.py
@@ -9,6 +9,7 @@ from django.contrib.auth.models import User
from datetime import datetime
+from postgresqleu.auth import user_search, user_import
from postgresqleu.util.decorators import ssl_required
from postgresqleu.confreg.models import Conference
@@ -89,6 +90,7 @@ def sponsor_manager_delete(request, sponsorid):
@ssl_required
@login_required
+@transaction.commit_on_success
def sponsor_manager_add(request, sponsorid):
sponsor = get_object_or_404(Sponsor, id=sponsorid, managers=request.user, confirmed=True)
@@ -102,7 +104,23 @@ def sponsor_manager_add(request, sponsorid):
messages.info(request, "User %s added as manager." % user.username)
return HttpResponseRedirect('../../')
except User.DoesNotExist:
- messages.warning(request, "Could not find user with email address %s" % request.POST['email'])
+ # Try an upstream search if the user is not here
+ users = user_search(request.POST['email'])
+ if len(users) == 1 and users[0]['e'] == request.POST['email']:
+ try:
+ user_import(users[0]['u'])
+ try:
+ u = User.objects.get(username=users[0]['u'])
+ sponsor.managers.add(u)
+ sponsor.save()
+ messages.info(request, "User with email %s imported as user %s." % (u.email, u.username))
+ messages.info(request, "User %s added as manager." % u.username)
+ except User.DoesNotExist:
+ messages.warning(request, "Failed to re-find user %s after import" % users[0]['u'])
+ except Exception, e:
+ messages.warning(request, "Failed to import user with email %s (userid %s): %s" % (users[0]['e'], users[0]['u'], e))
+ else:
+ messages.warning(request, "Could not find user with email address %s" % request.POST['email'])
return HttpResponseRedirect('../../')
@ssl_required
diff --git a/postgresqleu/urls.py b/postgresqleu/urls.py
index 471fae03..d3e6f159 100644
--- a/postgresqleu/urls.py
+++ b/postgresqleu/urls.py
@@ -160,6 +160,7 @@ urlpatterns = patterns('',
# Account info callbacks
(r'^accountinfo/search/$', postgresqleu.accountinfo.views.search),
+ (r'^accountinfo/import/$', postgresqleu.accountinfo.views.importuser),
# This should not happen in production - serve by apache!
url(r'^(favicon.ico)$', 'django.views.static.serve', {