diff options
Diffstat (limited to 'postgresqleu')
-rw-r--r-- | postgresqleu/accountinfo/views.py | 33 | ||||
-rw-r--r-- | postgresqleu/auth.py | 62 | ||||
-rw-r--r-- | postgresqleu/confsponsor/views.py | 20 | ||||
-rw-r--r-- | postgresqleu/urls.py | 1 |
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', { |