From: gfawcett Date: Fri, 16 Jul 2010 19:37:09 +0000 (+0000) Subject: Preliminary Sakai Linktool support X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=66f6be0f7d6c0e94f6a6fb70d315899eea7fb229;p=Syrup.git Preliminary Sakai Linktool support Merged git branch 'linktool-auth' (6aae15a) into 'master'. I used '--squash', which loses the merge history, but allows the merge to be committed to SVN ('linktool-auth' was not an SVN branch). git-svn-id: svn://svn.open-ils.org/ILS-Contrib/servres/trunk@928 6d9bc8c9-1ec2-4278-b937-99fde70a366f --- diff --git a/conifer/integration/auth_linktool.py b/conifer/integration/auth_linktool.py new file mode 100644 index 0000000..6983285 --- /dev/null +++ b/conifer/integration/auth_linktool.py @@ -0,0 +1,61 @@ +from django.contrib.auth.models import User +from django.conf import settings +from urllib import quote +from urllib2 import urlopen + +def testsign(query_string): + url = '%s?data=%s' % (settings.LINKTOOL_AUTH_URL, quote(query_string)) + result = urlopen(url).read() + return (result == 'true') + +class LinktoolAuthBackend(object): + + def __init__(self): + assert settings.LINKTOOL_AUTH_URL, \ + 'LinktoolAuthBackend requires settings.LINKTOOL_AUTH_URL' + + def authenticate(self, request=None): + valid = testsign(request.META['QUERY_STRING']) + if valid: + username = request.GET['user'] + return self.maybe_initialize_user(username) + return None + + def get_user(self, user_id): + try: + return User.objects.get(pk=user_id) + except User.DoesNotExist: + return None + + def maybe_initialize_user(self, username, look_local=True): + """Look up user in Django db; if not found, fetch user detail + from backend and set up a local user object. Return None if no + such user exists in either Django or the backend. + + Setting look_local=False skips the Django search and heads + straight to the backend; this shaves a database call when + walking a set of backends to initialize a user. Skipping + look_local on a username that already exists in Django will + certainly lead to an integrity error. + + This method is NOT part of the Django backend interface. + """ + user = None + if look_local: + try: + user = User.objects.get(username=username) + except User.DoesNotExist: + pass + if user is None: + u = self.lookup(username) + if u: # user found in LDAP or whatever. + user = User(username=username, + first_name = u['first_name'], + last_name = u['last_name'], + email = u['email']) + user.set_unusable_password() + user.save() + return user + + def lookup(self, username): + return None # fixme diff --git a/conifer/integration/clew-sections-for-site b/conifer/integration/clew-sections-for-site new file mode 100755 index 0000000..a4912bc --- /dev/null +++ b/conifer/integration/clew-sections-for-site @@ -0,0 +1,41 @@ +#!/Users/graham/bin/python-oracle-cgi-wrapper +# -*- mode: python -*- + +import os +import cx_Oracle +import re +import sys +import warnings +import simplejson as json +import time +import cgitb + +#cgitb.enable(display=True) + +conn = cx_Oracle.connect('sakai/PASSWORD@clew') + +try: + siteid = os.environ['PATH_INFO'][1:] + assert len(siteid) == 36, 'malformed site id' + + cursor = conn.cursor() + query = """ + select provider_id from sakai_realm + where realm_id like '/site/' || :1 + """ + cursor.execute(query, (siteid,)) + try: + provider_id = cursor.fetchone()[0] or '' + except: + raise Exception('site ID not found.') + + providers = provider_id.split('+') + output = json.dumps({'status':'ok', 'siteid': siteid, 'providers': providers}) +except Exception, e: + output = {'status':'error', 'error': str(e)} + +print 'Status: 200 OK' +print 'Content-Type: application/json' +print +print output +conn.close() diff --git a/conifer/integration/linktool/__init__.py b/conifer/integration/linktool/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/conifer/integration/linktool/app.py b/conifer/integration/linktool/app.py new file mode 100644 index 0000000..3cf9601 --- /dev/null +++ b/conifer/integration/linktool/app.py @@ -0,0 +1,14 @@ +from django.contrib.auth import authenticate, login +from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden + + +def linktool_welcome(request): + user = authenticate(request=request) + if user is None: + return HttpResponseForbidden('You are not allowed here.') + else: + login(request, user) + request.session['clew-site'] = request.GET['site'] + return HttpResponse("""""" + """Redirecting to the library system...""" % ( + request.META['SCRIPT_NAME'])) diff --git a/conifer/integration/linktool/backend.py b/conifer/integration/linktool/backend.py new file mode 100644 index 0000000..64020aa --- /dev/null +++ b/conifer/integration/linktool/backend.py @@ -0,0 +1,65 @@ +from conifer.plumbing.hooksystem import * +from django.conf import settings +from django.contrib.auth.models import User +from urllib import quote +from urllib2 import urlopen + + +def testsign(query_string): + url = '%s?data=%s' % (settings.LINKTOOL_AUTH_URL, quote(query_string)) + result = urlopen(url).read() + return (result == 'true') + + +class LinktoolAuthBackend(object): + + def __init__(self): + assert settings.LINKTOOL_AUTH_URL, \ + 'LinktoolAuthBackend requires settings.LINKTOOL_AUTH_URL' + + def authenticate(self, request=None): + valid = testsign(request.META['QUERY_STRING']) + if valid: + username = request.GET['user'] + return self.maybe_initialize_user(username) + return None + + def get_user(self, user_id): + try: + return User.objects.get(pk=user_id) + except User.DoesNotExist: + return None + + def maybe_initialize_user(self, username, look_local=True): + """Look up user in Django db; if not found, fetch user detail + from backend and set up a local user object. Return None if no + such user exists in either Django or the backend. + + Setting look_local=False skips the Django search and heads + straight to the backend; this shaves a database call when + walking a set of backends to initialize a user. Skipping + look_local on a username that already exists in Django will + certainly lead to an integrity error. + + This method is NOT part of the Django backend interface. + """ + user = None + if look_local: + try: + user = User.objects.get(username=username) + except User.DoesNotExist: + pass + if user is None: + u = self.lookup(username) + if u: # user found in LDAP or whatever. + user = User(username=username, + first_name = u['given_name'], + last_name = u['surname'], + email = u.get('email', None)) + user.set_unusable_password() + user.save() + return user + + def lookup(self, username): + return callhook('external_person_lookup', username) + diff --git a/conifer/integration/uwindsor.py b/conifer/integration/uwindsor.py index bcc137c..664a3ad 100644 --- a/conifer/integration/uwindsor.py +++ b/conifer/integration/uwindsor.py @@ -6,7 +6,7 @@ from conifer.libsystems.evergreen import item_status as I from conifer.libsystems.z3950 import pyz3950_search as PZ from xml.etree import ElementTree as ET import re - +import uwindsor_campus_info def department_course_catalogue(): """ @@ -111,3 +111,13 @@ def marcxml_to_url(marc_string): return None +def external_person_lookup(userid): + """ + Given a userid, return either None (if the user cannot be found), + or a dictionary representing the user. The dictionary must contain + the keys ('given_name', 'surname') and should contain 'email' if + an email address is known. + """ + return uwindsor_campus_info.call('person_lookup', userid) + + diff --git a/conifer/local_settings.py.example b/conifer/local_settings.py.example index adbe698..84ce294 100644 --- a/conifer/local_settings.py.example +++ b/conifer/local_settings.py.example @@ -31,9 +31,11 @@ TIME_ZONE = 'America/Detroit' SECRET_KEY = 'replace-with-your-own-super-random-key-@vv(tuvt2+yu2r-$dxs$s7=iqjz_s!&' #---------------------------------------------------------------------- +# Authentication systems EVERGREEN_AUTHENTICATION = False # Evergreen ILS authentication LINKTOOL_AUTHENTICATION = False # Sakai LMS Linktool authentication +LINKTOOL_AUTH_URL = 'https://...' # fixme, add documentation #---------------------------------------------------------------------- # Stuff that probably belongs in a config table in the database, with diff --git a/conifer/settings.py b/conifer/settings.py index 2494d06..02e7f6a 100644 --- a/conifer/settings.py +++ b/conifer/settings.py @@ -77,7 +77,7 @@ ROOT_URLCONF = 'conifer.urls' TEMPLATE_DIRS = [] -INSTALLED_APPS = ( +INSTALLED_APPS = [ 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', @@ -85,7 +85,7 @@ INSTALLED_APPS = ( 'django.contrib.admin', 'south', 'conifer.syrup', -) +] AUTH_PROFILE_MODULE = 'syrup.UserProfile' @@ -94,6 +94,9 @@ AUTHENTICATION_BACKENDS = [ 'django.contrib.auth.backends.ModelBackend' ] +EVERGREEN_AUTHENTICATION = False +LINKTOOL_AUTHENTICATION = False + #--------------------------------------------------------------------------- # local_settings.py @@ -120,5 +123,9 @@ if EVERGREEN_AUTHENTICATION: AUTHENTICATION_BACKENDS.append( 'conifer.integration.auth_evergreen.django.EvergreenAuthBackend') -#---------- +if LINKTOOL_AUTHENTICATION: + AUTHENTICATION_BACKENDS.append( + 'conifer.integration.linktool.backend.LinktoolAuthBackend') + INSTALLED_APPS.append( + 'conifer.integration.linktool.app') diff --git a/conifer/syrup/integration.py b/conifer/syrup/integration.py index 1b7a9d9..7ca2e3c 100644 --- a/conifer/syrup/integration.py +++ b/conifer/syrup/integration.py @@ -100,3 +100,12 @@ def marcxml_to_url(marc_string): codes and $u holds the URLs. """ +@disable +def external_person_lookup(userid): + """ + Given a userid, return either None (if the user cannot be found), + or a dictionary representing the user. The dictionary must contain + the keys ('given_name', 'surname') and should contain 'email' if + an email address is known. + """ + diff --git a/conifer/templates/tabbar.xhtml b/conifer/templates/tabbar.xhtml index 303dcad..2601e17 100644 --- a/conifer/templates/tabbar.xhtml +++ b/conifer/templates/tabbar.xhtml @@ -7,6 +7,9 @@ use one for now -->