From bcd6a421e8b53f7d1408950035e7c26ed5766c07 Mon Sep 17 00:00:00 2001 From: gfawcett Date: Wed, 18 Aug 2010 02:46:17 +0000 Subject: [PATCH] CAS authentication. See: http://code.google.com/p/django-cas/ To use CAS authentication, you must "easy_install django-cas", then add these to your local_settings.py: CAS_AUTHENTICATION = True CAS_SERVER_URL = 'https://my.cas.server.example.net/cas/' You will probably also want to define two customization hooks: external_person_lookup and user_needs_decoration. See: conifer/syrup/integration.py. git-svn-id: svn://svn.open-ils.org/ILS-Contrib/servres/trunk@969 6d9bc8c9-1ec2-4278-b937-99fde70a366f --- .gitignore | 1 + conifer/integration/cas.py | 53 +++++++++++++++++++++++++++++++++ conifer/integration/uwindsor.py | 3 +- conifer/plumbing/hooksystem.py | 4 +-- conifer/settings.py | 13 +++++++- conifer/syrup/integration.py | 11 +++++++ conifer/syrup/views/_common.py | 13 ++++---- conifer/syrup/views/genshi_namespace.py | 1 + conifer/templates/browse_index.xhtml | 2 +- conifer/templates/master.xhtml | 4 +-- conifer/templates/search_results.xhtml | 2 +- conifer/urls.py | 6 ++++ 12 files changed, 100 insertions(+), 13 deletions(-) create mode 100644 conifer/integration/cas.py diff --git a/.gitignore b/.gitignore index 63dd581..4def612 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ private_local_settings.py /conifer/remodel.sqlite3 *~ /conifer/test.db +/conifer/syrup/test.db diff --git a/conifer/integration/cas.py b/conifer/integration/cas.py new file mode 100644 index 0000000..c0a9fd3 --- /dev/null +++ b/conifer/integration/cas.py @@ -0,0 +1,53 @@ +# CAS authentication. See http://code.google.com/p/django-cas/ +# +# To use CAS authentication, you must "easy_install django-cas", then add these +# to your local_settings.py: +# +# CAS_AUTHENTICATION = True +# CAS_SERVER_URL = 'https://my.cas.server.example.net/cas/' +# +# You will probably also want to define two customization hooks: +# external_person_lookup and user_needs_decoration. See: +# conifer/syrup/integration.py. + +from conifer.plumbing.hooksystem import gethook, callhook +import django_cas.backends + + +class CASBackend(django_cas.backends.CASBackend): + + def authenticate(self, ticket, service): + """Authenticates CAS ticket and retrieves user data""" + + user = super(CASBackend, self).authenticate(ticket, service) + if user and gethook('external_person_lookup'): + decorate_user(user) + return user + + +# TODO is this really CAS specific? Wouldn't linktool (for example) +# also need such a decorator? + +def decorate_user(user): + dectest = gethook('user_needs_decoration', default=_user_needs_decoration) + if not dectest(user): + return + + dir_entry = callhook('external_person_lookup', user.username) + if dir_entry is None: + return + + user.first_name = dir_entry['given_name'] + user.last_name = dir_entry['surname'] + user.email = dir_entry.get('email', user.email) + user.save() + + if 'patron_id' in dir_entry: + # note, we overrode user.get_profile() to automatically create + # missing profiles. See models.py. + user.get_profile().ils_userid = dir_entry['patron_id'] + profile.save() + + +def _user_needs_decoration(user): + return user.last_name is not None diff --git a/conifer/integration/uwindsor.py b/conifer/integration/uwindsor.py index b094531..0419ced 100644 --- a/conifer/integration/uwindsor.py +++ b/conifer/integration/uwindsor.py @@ -135,7 +135,8 @@ 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. + an email address is known, and 'patron_id' if a library-system ID + is known. """ return uwindsor_campus_info.call('person_lookup', userid) diff --git a/conifer/plumbing/hooksystem.py b/conifer/plumbing/hooksystem.py index 5144499..935b592 100644 --- a/conifer/plumbing/hooksystem.py +++ b/conifer/plumbing/hooksystem.py @@ -6,8 +6,6 @@ import conifer.syrup.integration as HOOKS __all__ = ['callhook', 'callhook_required', 'gethook'] def gethook(name, default=None): - print dir(HOOKS) - print (name, getattr(HOOKS, name)) return getattr(HOOKS, name) or default def callhook_required(name, *args, **kwargs): @@ -19,3 +17,5 @@ def callhook(name, *args, **kwargs): f = getattr(HOOKS, name) if f: return f(*args, **kwargs) + else: + return None diff --git a/conifer/settings.py b/conifer/settings.py index 3783636..07c6f16 100644 --- a/conifer/settings.py +++ b/conifer/settings.py @@ -87,8 +87,10 @@ INSTALLED_APPS = [ 'conifer.syrup', ] -AUTH_PROFILE_MODULE = 'syrup.UserProfile' +LOGIN_URL = '/accounts/login/' +LOGOUT_URL = '/accounts/logout' +AUTH_PROFILE_MODULE = 'syrup.UserProfile' AUTHENTICATION_BACKENDS = [ 'django.contrib.auth.backends.ModelBackend' @@ -97,6 +99,10 @@ AUTHENTICATION_BACKENDS = [ EVERGREEN_AUTHENTICATION = False LINKTOOL_AUTHENTICATION = False +# CAS authentication requires 'django-cas', +# http://code.google.com/p/django-cas/ +CAS_AUTHENTICATION = False + #--------------------------------------------------------------------------- # local_settings.py @@ -126,3 +132,8 @@ if EVERGREEN_AUTHENTICATION: if LINKTOOL_AUTHENTICATION: AUTHENTICATION_BACKENDS.append( 'conifer.integration.linktool.backend.LinktoolAuthBackend') + +if CAS_AUTHENTICATION: + AUTHENTICATION_BACKENDS.append('conifer.integration.cas.CASBackend') + LOGIN_URL = '/cas/login' + LOGOUT_URL = '/cas/logout' diff --git a/conifer/syrup/integration.py b/conifer/syrup/integration.py index 7ca2e3c..eff6b98 100644 --- a/conifer/syrup/integration.py +++ b/conifer/syrup/integration.py @@ -109,3 +109,14 @@ def external_person_lookup(userid): an email address is known. """ +@disable +def user_needs_decoration(user_obj): + """ + User objects are sometimes created automatically, with only a + username. This function determines whether it would be fruitful to + "decorate" the User object with, e.g., a given name, surname, and + email address. It doesn't perform the decoration, it simply tests + whether the current user object is "incomplete." Another hook + 'external_person_lookup,' is used by Syrup to fetch the personal + information when needed. + """ diff --git a/conifer/syrup/views/_common.py b/conifer/syrup/views/_common.py index 00fba9a..2161816 100644 --- a/conifer/syrup/views/_common.py +++ b/conifer/syrup/views/_common.py @@ -4,9 +4,11 @@ # is a module which acts as a global namespace when expanding a Genshi # template. +from . import genshi_namespace from conifer.here import HERE from conifer.plumbing.genshi_support import TemplateSet -from . import genshi_namespace +from django.conf import settings + g = TemplateSet(HERE('templates'), genshi_namespace) @@ -48,10 +50,11 @@ __builtins__['_'] = translation.ugettext def _access_denied(request, message): if request.user.is_anonymous(): # then take them to login screen.... - dest = (request.META['SCRIPT_NAME'] + \ - '/accounts/login/?next=%s%s' % ( - request.META['SCRIPT_NAME'], - request.META['PATH_INFO'])) + dest = (request.META['SCRIPT_NAME'] + + settings.LOGIN_URL + + '?next=' + + request.META['SCRIPT_NAME'] + + request.META['PATH_INFO']) return HttpResponseRedirect(dest) else: return simple_message(_('Access denied.'), message, diff --git a/conifer/syrup/views/genshi_namespace.py b/conifer/syrup/views/genshi_namespace.py index 5186685..431202c 100644 --- a/conifer/syrup/views/genshi_namespace.py +++ b/conifer/syrup/views/genshi_namespace.py @@ -13,4 +13,5 @@ import urllib from conifer.plumbing.hooksystem import gethook, callhook from conifer.syrup import models +from django.conf import settings from django.utils.translation import ugettext as _ diff --git a/conifer/templates/browse_index.xhtml b/conifer/templates/browse_index.xhtml index e31edf5..814bbb3 100644 --- a/conifer/templates/browse_index.xhtml +++ b/conifer/templates/browse_index.xhtml @@ -15,7 +15,7 @@ blocks = itertools.groupby(sites, lambda s: s.course.department)

${title}

(Note: some reserve materials may require you - to log in) + to log in)
diff --git a/conifer/templates/master.xhtml b/conifer/templates/master.xhtml index c885ebe..fe0eed6 100644 --- a/conifer/templates/master.xhtml +++ b/conifer/templates/master.xhtml @@ -51,12 +51,12 @@ import os
Welcome, ${user.first_name or user.username}! - Log Out + Log OutPreferences
Welcome! - Log In + Log InPreferences
diff --git a/conifer/templates/search_results.xhtml b/conifer/templates/search_results.xhtml index 2a27f2c..bb0b8e1 100644 --- a/conifer/templates/search_results.xhtml +++ b/conifer/templates/search_results.xhtml @@ -53,7 +53,7 @@ title = _('Search Results')
Your searches may return more results if you log in before + href="${ROOT}${settings.LOGIN_URL}?next=${ROOT}/">log in before searching.
diff --git a/conifer/urls.py b/conifer/urls.py index ec9578c..4ee3e13 100644 --- a/conifer/urls.py +++ b/conifer/urls.py @@ -47,3 +47,9 @@ if settings.LINKTOOL_AUTHENTICATION: (r'^linktool-welcome/copy_old$', 'linktool_copy_old'), (r'^linktool-welcome/associate$', 'linktool_associate'), ) + +if settings.CAS_AUTHENTICATION: + urlpatterns += patterns( + 'django_cas.views', + (r'^%s$' % settings.LOGIN_URL[1:], 'login'), + (r'^%s$' % settings.LOGOUT_URL[1:], 'logout')) -- 2.11.0