added Django authentication-backend
authorgfawcett <gfawcett@6d9bc8c9-1ec2-4278-b937-99fde70a366f>
Sun, 1 Mar 2009 03:29:48 +0000 (03:29 +0000)
committergfawcett <gfawcett@6d9bc8c9-1ec2-4278-b937-99fde70a366f>
Sun, 1 Mar 2009 03:29:48 +0000 (03:29 +0000)
It authenticates users against an Evergreen server via XMLRPC (for
simplicity: XMLRPC requires no third-party modules). Just set the
EVERGREEN_XMLRPC_SERVER to a valid host and away you go.

git-svn-id: svn://svn.open-ils.org/ILS-Contrib/servres/trunk@129 6d9bc8c9-1ec2-4278-b937-99fde70a366f

conifer/custom/__init__.py [new file with mode: 0644]
conifer/custom/auth_evergreen.py [new file with mode: 0644]
conifer/custom/auth_evergreen_support.py [new file with mode: 0644]
conifer/settings.py

diff --git a/conifer/custom/__init__.py b/conifer/custom/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/conifer/custom/auth_evergreen.py b/conifer/custom/auth_evergreen.py
new file mode 100644 (file)
index 0000000..23d8c34
--- /dev/null
@@ -0,0 +1,31 @@
+from auth_evergreen_support import EvergreenAuthServer
+from django.contrib.auth.models import User
+from django.conf import settings
+
+class EvergreenAuthBackend(EvergreenAuthServer):
+
+    def __init__(self):
+        EvergreenAuthServer.__init__(
+            self, settings.EVERGREEN_XMLRPC_SERVER)
+
+    def authenticate(self, username=None, password=None):
+        pwd_valid = self.login(username, password)
+        if pwd_valid:
+            try:
+                user = User.objects.get(username=username)
+            except User.DoesNotExist:
+                u = self.lookup(username)
+                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
+        return None
+
+    def get_user(self, user_id):
+        try:
+            return User.objects.get(pk=user_id)
+        except User.DoesNotExist:
+            return None
diff --git a/conifer/custom/auth_evergreen_support.py b/conifer/custom/auth_evergreen_support.py
new file mode 100644 (file)
index 0000000..da2819c
--- /dev/null
@@ -0,0 +1,86 @@
+# auth_evergreen_support -- Authentication and user lookup against an
+# Evergreen XML-RPC server.
+
+# This is the Evergreen-specific stuff, with no Django dependencies.
+
+import xmlrpclib
+import md5
+import warnings
+import time
+
+#----------------------------------------------------------------------
+# support
+
+def do_request(proxy, method, *args):
+    # Against my test server, I would get intermittent
+    # ProtcolErrors. If we get one, try again, backing off gradually.
+    for attempt in range(5):
+        try:
+            return getattr(proxy, method)(*args)
+        except xmlrpclib.ProtocolError, pe:
+            warnings.warn('open-ils xml-rpc protocol error: trying again: ' + method)
+            time.sleep(0.1 * attempt)       # back off a bit and try again
+
+def _hsh(s):
+    return md5.new(s).hexdigest()
+
+#----------------------------------------------------------------------
+# main interface
+
+class EvergreenAuthServer(object):
+
+    def __init__(self, address, verbose=False):
+        self.address = address
+        self.verbose = verbose
+
+    def proxy(self, service):
+        server = xmlrpclib.Server(
+            'http://%s/xml-rpc/%s' % (self.address, service),
+            verbose=self.verbose)
+        def req(method, *args):
+            return do_request(server, method, *args)
+        return req
+
+    def login(self, username, password):
+        """Return True if the username/password are good, False otherwise."""
+        prx = self.proxy('open-ils.auth')
+        seed = prx('open-ils.auth.authenticate.init', username)
+        resp = prx('open-ils.auth.authenticate.complete',
+                   dict(username='admin',
+                        password=_hsh(seed + _hsh(password)), 
+                        type='reserves'))
+        try:
+            # do we need the authkey for anything?
+            authkey = resp['payload']['authtoken']
+            return True
+        except KeyError:
+            return False
+
+    def lookup(self, username):
+        """Given a username, return a dict, or None. The dict must have
+        four keys (first_name, last_name, email, external_username), where
+        external_username value is the username parameter."""
+
+        prx = self.proxy('open-ils.actor')
+        r = prx('open-ils.actor.user.search.username', 'admin')
+        if not r:
+            return None
+        else:
+            r = r[0]['__data__']
+            f = lambda k: r.get(k)
+            person = dict((j, f(k)) for j,k in [('first_name', 'first_given_name'),
+                                                ('last_name', 'family_name'),
+                                                ('email', 'email'),
+                                                ('external_username', 'usrname')])
+            return person
+
+#----------------------------------------------------------------------
+# testing
+
+if __name__ == '__main__':
+    from pprint import pprint
+    address = '192.168.1.10'
+    egreen = EvergreenAuthServer(address)
+    username, password = 'admin', 'open-ils'
+    print egreen.login(username, password)
+    pprint(egreen.lookup('admin'))
index 415cc47..c1b181d 100644 (file)
@@ -92,3 +92,14 @@ INSTALLED_APPS = (
 )
 
 AUTH_PROFILE_MODULE = 'syrup.UserProfile'
+
+
+AUTHENTICATION_BACKENDS = [
+    'django.contrib.auth.backends.ModelBackend',
+]
+
+EVERGREEN_XMLRPC_SERVER = None # evergreen host, for auth, e.g. '192.168.1.10'
+
+if EVERGREEN_XMLRPC_SERVER:
+    AUTHENTICATION_BACKENDS.append(
+        'conifer.custom.auth_evergreen.EvergreenAuthBackend')