[snowy] Adding some files for django_openid_auth I forgot



commit f7ed4e60992907c9a46cb661005b52260f7704f8
Author: Jeff Schroeder <jeffschroeder computer org>
Date:   Sun Oct 9 20:53:30 2011 -0700

    Adding some files for django_openid_auth I forgot

 lib/django_openid_auth/MANIFEST.in                 |    8 +
 lib/django_openid_auth/Makefile                    |   10 ++
 lib/django_openid_auth/README.txt                  |  168 ++++++++++++++++++++
 .../example_consumer/__init__.py                   |   28 ++++
 .../example_consumer/settings.py                   |  143 +++++++++++++++++
 lib/django_openid_auth/example_consumer/urls.py    |   45 ++++++
 lib/django_openid_auth/example_consumer/views.py   |   57 +++++++
 lib/django_openid_auth/exceptions.py               |   68 ++++++++
 lib/django_openid_auth/openid.html                 |  150 +++++++++++++++++
 lib/django_openid_auth/openid.txt                  |  165 +++++++++++++++++++
 lib/django_openid_auth/setup.py                    |   80 +++++++++
 lib/django_openid_auth/signals.py                  |   34 ++++
 lib/django_openid_auth/tests/test_auth.py          |  155 ++++++++++++++++++
 13 files changed, 1111 insertions(+), 0 deletions(-)
---
diff --git a/lib/django_openid_auth/MANIFEST.in b/lib/django_openid_auth/MANIFEST.in
new file mode 100644
index 0000000..acc52f6
--- /dev/null
+++ b/lib/django_openid_auth/MANIFEST.in
@@ -0,0 +1,8 @@
+include Makefile
+include MANIFEST.in
+include LICENSE.txt
+include README.txt
+include TODO.txt
+
+recursive-include django_openid_auth/templates *.html
+recursive-include example_consumer *.py
diff --git a/lib/django_openid_auth/Makefile b/lib/django_openid_auth/Makefile
new file mode 100644
index 0000000..d43e6cb
--- /dev/null
+++ b/lib/django_openid_auth/Makefile
@@ -0,0 +1,10 @@
+
+check:
+	PYTHONPATH=$(shell pwd) python example_consumer/manage.py test \
+	   --verbosity=2 django_openid_auth
+
+run-example-consumer:
+	PYTHONPATH=$(shell pwd) python example_consumer/manage.py syncdb
+	PYTHONPATH=$(shell pwd) python example_consumer/manage.py runserver
+
+.PHONY: check run-example-consumer
diff --git a/lib/django_openid_auth/README.txt b/lib/django_openid_auth/README.txt
new file mode 100644
index 0000000..257ea09
--- /dev/null
+++ b/lib/django_openid_auth/README.txt
@@ -0,0 +1,168 @@
+= Django OpenID Authentication Support =
+
+This package provides integration between Django's authentication
+system and OpenID authentication.  It also includes support for using
+a fixed OpenID server endpoint, which can be useful when implementing
+single signon systems.
+
+
+== Basic Installation ==
+
+ 1. Install the Jan Rain Python OpenID library.  It can be found at:
+
+        http://openidenabled.com/python-openid/
+
+    It can also be found in most Linux distributions packaged as
+    "python-openid".  You will need version 2.2.0 or later.
+
+ 2. Add 'django_openid_auth' to INSTALLED_APPS for your application.
+    At a minimum, you'll need the following in there:
+
+        INSTALLED_APPS = (
+            'django.contrib.auth',
+            'django.contrib.contenttypes',
+            'django.contrib.sessions',
+            'django_openid_auth',
+        )
+
+ 3. Add 'django_auth_openid.auth.OpenIDBackend' to
+    AUTHENTICATION_BACKENDS.  This should be in addition to the
+    default ModelBackend:
+
+        AUTHENTICATION_BACKENDS = (
+            'django_openid_auth.auth.OpenIDBackend',
+            'django.contrib.auth.backends.ModelBackend',
+        )
+
+ 4. To create users automatically when a new OpenID is used, add the
+    following to the settings:
+
+        OPENID_CREATE_USERS = True
+
+ 5. To have user details updated from OpenID Simple Registration or
+    Attribute Exchange extension data each time they log in, add the
+    following:
+
+        OPENID_UPDATE_DETAILS_FROM_SREG = True
+
+ 6. Hook up the login URLs to your application's urlconf with
+    something like:
+
+        urlpatterns = patterns('',
+            ...
+            (r'^openid/', include('django_openid_auth.urls')),
+            ...
+        )
+
+ 7. Configure the LOGIN_URL and LOGIN_REDIRECT_URL appropriately for
+    your site:
+
+        LOGIN_URL = '/openid/login/'
+        LOGIN_REDIRECT_URL = '/'
+
+    This will allow pages that use the standard @login_required
+    decorator to use the OpenID login page.
+
+ 8. Rerun "python manage.py syncdb" to add the UserOpenID table to
+    your database.
+
+
+== Configuring Single Sign-On ==
+
+If you only want to accept identities from a single OpenID server and
+that server implemnts OpenID 2.0 identifier select mode, add the
+following setting to your app:
+
+    OPENID_SSO_SERVER_URL = 'server-endpoint-url'
+
+With this setting enabled, the user will not be prompted to enter
+their identity URL, and instead an OpenID authentication request will
+be started with the given server URL.
+
+As an example, to use Launchpad accounts for SSO, you'd use:
+
+     OPENID_SSO_SERVER_URL = 'https://login.launchpad.net/'
+
+
+== Launchpad Teams Support ==
+
+This library supports the Launchpad Teams OpenID extension.  Using
+this feature, it is possible to map Launchpad team memberships to
+Django group memberships.  It can be configured with:
+
+    OPENID_SSO_SERVER_URL = 'https://login.launchpad.net/'
+    OPENID_LAUNCHPAD_TEAMS_MAPPING = {
+        'launchpad-team-1': 'django-group-1',
+        'launchpad-team-2': 'django-group-2',
+        }
+
+When a user logs in, they will be added or removed from the relevant
+teams listed in the mapping.
+
+If you have already django-groups and want to map these groups automatically, you can use the OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO variable in your settings.py file.
+
+	OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO = True
+
+If you use OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO, the variable OPENID_LAUNCHPAD_TEAMS_MAPPING will be ignored.
+If you want to exclude some groups from the auto mapping, use OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO_BLACKLIST. This variable has only an effect if OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO is True.
+
+	OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO_BLACKLIST = ['django-group1', 'django-group2']
+	
+== External redirect domains ==
+
+By default, redirecting back to an external URL after auth is forbidden. To permit redirection to external URLs on a separate domain, define ALLOWED_EXTERNAL_OPENID_REDIRECT_DOMAINS in your settings.py file as a list of permitted domains:
+
+	ALLOWED_EXTERNAL_OPENID_REDIRECT_DOMAINS = ['example.com', 'example.org']
+
+and redirects to external URLs on those domains will additionally be permitted.
+
+== Use as /admin (django.admin.contrib) login ==
+
+If you require openid authentication into the admin application, add the following setting:
+
+        OPENID_USE_AS_ADMIN_LOGIN = True
+
+It is worth noting that a user needs to be be marked as a "staff user" to be able to access the admin interface.  A new openid user will not normally be a "staff user".  
+The easiest way to resolve this is to use traditional authentication (OPENID_USE_AS_ADMIN_LOGIN = False) to sign in as your first user with a password and authorise your 
+openid user to be staff.
+
+== Change Django usernames if the nickname changes on the provider ==
+
+If you want your Django username to change when a user updates the nickname on their provider, add the following setting:
+
+        OPENID_FOLLOW_RENAMES = True
+
+If the new nickname is available as a Django username, the user is renamed.
+Otherwise the user will be renamed to nickname+i for an incrememnting value of i until no conflict occurs.
+If the user has already been renamed to nickname+1 due to a conflict, and the nickname is still not available, the user will keep their existing username.
+
+== Require a valid nickname ==
+
+If you must have a valid, unique nickname in order to create a user accont, add the following setting:
+
+        OPENID_STRICT_USERNAMES = True
+        
+This will cause an OpenID login attempt to fail if the provider does not return a 'nickname' (username) for the user, or if the nickname conflicts with an existing user with a different openid identiy url.
+Without this setting, logins without a nickname will be given the username 'openiduser', and upon conflicts with existing username, an incrementing number will be appended to the username until it is unique.
+
+== Require Physical Multi-Factor Authentication ==
+
+If your users should use a physical multi-factor authentication method, such as RSA tokens or YubiKey, add the following setting:
+
+        OPENID_PHYSICAL_MULTIFACTOR_REQUIRED = True
+        
+If the user's OpenID provider supports the PAPE extension and provides the Physical Multifactor authentication policy, this will
+cause the OpenID login to fail if the user does not provide valid physical authentication to the provider.
+
+== Override Login Failure Handling ==
+
+You can optionally provide your own handler for login failures by adding the following setting:
+
+        OPENID_RENDER_FAILURE = failure_handler_function
+
+Where failure_handler_function is a function reference that will take the following parameters:
+
+        def failure_handler_function(request, message, status=None, template_name=None, exception=None)
+
+This function must return a Django.http.HttpResponse instance.
+
diff --git a/lib/django_openid_auth/TODO.txt b/lib/django_openid_auth/TODO.txt
new file mode 100644
index 0000000..e69de29
diff --git a/lib/django_openid_auth/example_consumer/__init__.py b/lib/django_openid_auth/example_consumer/__init__.py
new file mode 100644
index 0000000..956164e
--- /dev/null
+++ b/lib/django_openid_auth/example_consumer/__init__.py
@@ -0,0 +1,28 @@
+# django-openid-auth -  OpenID integration for django.contrib.auth
+#
+# Copyright (C) 2007 Simon Willison
+# Copyright (C) 2008-2010 Canonical Ltd.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
diff --git a/lib/django_openid_auth/example_consumer/settings.py b/lib/django_openid_auth/example_consumer/settings.py
new file mode 100644
index 0000000..33ea110
--- /dev/null
+++ b/lib/django_openid_auth/example_consumer/settings.py
@@ -0,0 +1,143 @@
+# django-openid-auth -  OpenID integration for django.contrib.auth
+#
+# Copyright (C) 2007 Simon Willison
+# Copyright (C) 2008-2010 Canonical Ltd.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+# Django settings for example project.
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+    # ('Your Name', 'your_email domain com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASE_ENGINE = 'sqlite3'           # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql'.
+DATABASE_NAME = 'sqlite.db'             # Or path to database file if using sqlite3.
+DATABASE_USER = ''             # Not used with sqlite3.
+DATABASE_PASSWORD = ''         # Not used with sqlite3.
+DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3.
+DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
+
+# Local time zone for this installation. Choices can be found here:
+# http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
+# although not all variations may be possible on all operating systems.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'America/Chicago'
+
+# Language code for this installation. All choices can be found here:
+# http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
+# http://blogs.law.harvard.edu/tech/stories/storyReader$15
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# Absolute path to the directory that holds media.
+# Example: "/home/media/media.lawrence.com/"
+MEDIA_ROOT = ''
+
+# URL that handles the media served from MEDIA_ROOT.
+# Example: "http://media.lawrence.com";
+MEDIA_URL = ''
+
+# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
+# trailing slash.
+# Examples: "http://foo.com/media/";, "/media/".
+ADMIN_MEDIA_PREFIX = '/media/'
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = '34958734985734985734985798437'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.load_template_source',
+    'django.template.loaders.app_directories.load_template_source',
+#     'django.template.loaders.eggs.load_template_source',
+)
+
+# django-openid-auth will *not* work with Django 1.1.1 or older, as it's
+# missing the csrf_token template tag.  This will allow it to work with
+# Django 1.1.2 or later:
+try: 
+    import django.middleware.csrf
+except ImportError:
+    csrf_middleware = 'django.contrib.csrf.middleware.CsrfViewMiddleware'
+else:
+    csrf_middleware = 'django.middleware.csrf.CsrfViewMiddleware'
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    csrf_middleware,
+)
+
+ROOT_URLCONF = 'example_consumer.urls'
+
+TEMPLATE_DIRS = (
+    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+)
+
+INSTALLED_APPS = (
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.admin',
+    'django_openid_auth',
+)
+
+AUTHENTICATION_BACKENDS = (
+    'django_openid_auth.auth.OpenIDBackend',
+    'django.contrib.auth.backends.ModelBackend',
+)
+
+# Should users be created when new OpenIDs are used to log in?
+OPENID_CREATE_USERS = True
+
+# When logging in again, should we overwrite user details based on
+# data received via Simple Registration?
+OPENID_UPDATE_DETAILS_FROM_SREG = True
+
+# If set, always use this as the identity URL rather than asking the
+# user.  This only makes sense if it is a server URL.
+OPENID_SSO_SERVER_URL = 'https://login.launchpad.net/'
+
+# Tell django.contrib.auth to use the OpenID signin URLs.
+LOGIN_URL = '/openid/login/'
+LOGIN_REDIRECT_URL = '/'
+
+# Should django_auth_openid be used to sign into the admin interface?
+OPENID_USE_AS_ADMIN_LOGIN = False
diff --git a/lib/django_openid_auth/example_consumer/urls.py b/lib/django_openid_auth/example_consumer/urls.py
new file mode 100644
index 0000000..74d2ec3
--- /dev/null
+++ b/lib/django_openid_auth/example_consumer/urls.py
@@ -0,0 +1,45 @@
+# django-openid-auth -  OpenID integration for django.contrib.auth
+#
+# Copyright (C) 2007 Simon Willison
+# Copyright (C) 2008-2010 Canonical Ltd.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+from django.conf.urls.defaults import *
+from django.contrib import admin
+
+import views
+
+
+admin.autodiscover()
+
+urlpatterns = patterns('',
+    (r'^$', views.index),
+    (r'^openid/', include('django_openid_auth.urls')),
+    (r'^logout/$', 'django.contrib.auth.views.logout'),
+    (r'^private/$', views.require_authentication),
+
+    (r'^admin/(.*)', admin.site.root),
+)
diff --git a/lib/django_openid_auth/example_consumer/views.py b/lib/django_openid_auth/example_consumer/views.py
new file mode 100644
index 0000000..3e8c3ed
--- /dev/null
+++ b/lib/django_openid_auth/example_consumer/views.py
@@ -0,0 +1,57 @@
+# django-openid-auth -  OpenID integration for django.contrib.auth
+#
+# Copyright (C) 2007 Simon Willison
+# Copyright (C) 2008-2010 Canonical Ltd.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+from django.contrib.auth.decorators import login_required
+from django.http import HttpResponse
+from django.utils.html import escape
+
+
+def index(request):
+    s = ['<p>']
+    if request.user.is_authenticated():
+        s.append('You are signed in as <strong>%s</strong> (%s)' % (
+                escape(request.user.username),
+                escape(request.user.get_full_name())))
+        s.append(' | <a href="/logout">Sign out</a>')
+    else:
+        s.append('<a href="/openid/login">Sign in with OpenID</a>')
+
+    s.append('</p>')
+
+    s.append('<p><a href="/private">This requires authentication</a></p>')
+    return HttpResponse('\n'.join(s))
+
+
+def next_works(request):
+    return HttpResponse('?next= bit works. <a href="/">Home</a>')
+
+
+ login_required
+def require_authentication(request):
+    return HttpResponse('This page requires authentication')
diff --git a/lib/django_openid_auth/exceptions.py b/lib/django_openid_auth/exceptions.py
new file mode 100644
index 0000000..08c970c
--- /dev/null
+++ b/lib/django_openid_auth/exceptions.py
@@ -0,0 +1,68 @@
+# django-openid-auth -  OpenID integration for django.contrib.auth
+#
+# Copyright (C) 2008-2010 Canonical Ltd.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+"""Exception classes thrown by OpenID Authentication and Validation."""
+
+class DjangoOpenIDException(Exception):
+    pass
+
+class RequiredAttributeNotReturned(DjangoOpenIDException):
+    pass
+
+class IdentityAlreadyClaimed(DjangoOpenIDException):
+
+    def __init__(self, message=None):
+        if message is None:
+            self.message = "Another user already exists for your selected OpenID"
+        else:
+            self.message = message
+
+class DuplicateUsernameViolation(DjangoOpenIDException):
+
+    def __init__(self, message=None):
+        if message is None:
+            self.message = "Your desired username is already being used."
+        else:
+            self.message = message
+
+class MissingUsernameViolation(DjangoOpenIDException):
+
+    def __init__(self, message=None):
+        if message is None:
+            self.message = "No nickname given for your account."
+        else:
+            self.message = message
+
+class MissingPhysicalMultiFactor(DjangoOpenIDException):
+
+    def __init__(self, message=None):
+        if message is None:
+            self.message = "Login requires physical multi-factor authentication."
+        else:
+            self.message = message
+
diff --git a/lib/django_openid_auth/openid.html b/lib/django_openid_auth/openid.html
new file mode 100644
index 0000000..cd8004f
--- /dev/null
+++ b/lib/django_openid_auth/openid.html
@@ -0,0 +1,150 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+  "http://www.w3.org/TR/html4/strict.dtd";>
+<html>
+<head><title>OpenID in Django</title></head>
+<body>
+<h1>OpenID in Django</h1>
+<p>The <tt class="docutils literal"><span class="pre">django_openidconsumer</span></tt> package contains all of the code needed to set up
+your Django application as an OpenID consumer. You can use it to allow OpenID
+users to sign in to your site without having to create a new username and
+password.</p>
+<div class="section">
+<h2><a id="overview">Overview</a></h2>
+<p>The OpenID consumer system consists of:</p>
+<ul class="simple">
+<li>Views for you to hook in to your application.</li>
+<li>Database models implementing the persistence layer of an OpenID consumer.</li>
+<li>Middleware that makes <tt class="docutils literal"><span class="pre">request.openid</span></tt> and <tt class="docutils literal"><span class="pre">request.openids</span></tt>
+properties available to your application views.</li>
+</ul>
+</div>
+<div class="section">
+<h2><a id="dependencies">Dependencies</a></h2>
+<p><tt class="docutils literal"><span class="pre">django_openidconsumer</span></tt> uses the <a class="reference" href="http://www.openidenabled.com/openid/libraries/python/";>python-openid library</a>, which must be
+installed separately somewhere on the Python path. You should install the 1.2.0
+&#8220;combo&#8221; package which includes the <tt class="docutils literal"><span class="pre">yadis</span></tt> and <tt class="docutils literal"><span class="pre">urljr</span></tt> libraries.</p>
+<p>The package also depends on the availability of Django&#8217;s <a class="reference" href="http://www.djangoproject.com/documentation/sessions/";>session support</a>.</p>
+</div>
+<div class="section">
+<h2><a id="installation">Installation</a></h2>
+<p>Having ensured that both the  <tt class="docutils literal"><span class="pre">python-openid</span></tt> library and the <tt class="docutils literal"><span class="pre">django_openidconsumer</span></tt> package are available on your Python path, you can
+add OpenID consumer support to an application by doing the following:</p>
+<ol class="arabic">
+<li><p class="first">Put <tt class="docutils literal"><span class="pre">django_openidconsumer</span></tt> in your <tt class="docutils literal"><span class="pre">INSTALLED_APPS</span></tt> setting.</p>
+</li>
+<li><p class="first">Run the command <tt class="docutils literal"><span class="pre">manage.py</span> <span class="pre">syncdb</span></tt> to create the necessary tables.</p>
+</li>
+<li><p class="first">Add <tt class="docutils literal"><span class="pre">django_openidconsumer.middleware.OpenIDMiddleware</span></tt> to your list
+of <tt class="docutils literal"><span class="pre">MIDDLEWARE_CLASSES</span></tt>, somewhere after the Session middleware.</p>
+</li>
+<li><p class="first">Add the following views to your urlconf:</p>
+<pre class="literal-block">
+(r'^openid/$', 'django_openidconsumer.views.begin'),
+(r'^openid/complete/$', 'django_openidconsumer.views.complete'),
+(r'^openid/signout/$', 'django_openidconsumer.views.signout'),
+</pre>
+</li>
+</ol>
+<p>You will then be able to browse to <tt class="docutils literal"><span class="pre">example.com/openid/</span></tt> and sign in using
+an OpenID.</p>
+</div>
+<div class="section">
+<h2><a id="using-the-openid-middleware">Using the OpenID middleware</a></h2>
+<p>With the Middleware installed, your views will have access to the user&#8217;s OpenID
+as the <tt class="docutils literal"><span class="pre">request.openid</span></tt> property. This will be <tt class="docutils literal"><span class="pre">None</span></tt> if the user has not
+yet authenticated; otherwise it will be a <tt class="docutils literal"><span class="pre">django_openidconsumer.util.OpenID</span></tt>
+instance.</p>
+<p>If you want the user&#8217;s OpenID as a string, call the <tt class="docutils literal"><span class="pre">str()</span></tt> builtin on the
+OpenID instance:</p>
+<pre class="literal-block">
+def example_view(request):
+    if request.openid:
+        return HttpResponse(&quot;OpenID is %s&quot; % escape(str(request.openid)))
+    else:
+        return HttpResponse(&quot;No OpenID&quot;)
+</pre>
+<p>Users can sign in with more than one OpenID. This is supported by the
+<tt class="docutils literal"><span class="pre">request.openids</span></tt> property, which is a list of <tt class="docutils literal"><span class="pre">OpenID</span></tt> objects in the order
+in which they were authenticated. <tt class="docutils literal"><span class="pre">request.openid</span></tt> merely returns the last
+item in this list.</p>
+</div>
+<div class="section">
+<h2><a id="using-simple-registration">Using simple registration</a></h2>
+<p>Simple registration (or <a class="reference" href="http://openid.net/specs/openid-simple-registration-extension-1_0.html";>sreg</a>) is an extension to the OpenID specification
+that  allows you to request extra details about a user from their OpenID
+provider. It is frequently used to pre-populate registration forms with
+information such as the user&#8217;s name, e-mail address or date of birth.</p>
+<p>Be aware that not all OpenID providers support sreg, and there is no guarantee
+that the information you have requested will be returned. Simple registration
+should be used as a convenience for your users rather than as a required step in
+your authentication process.</p>
+<p>Available simple registration fields are <tt class="docutils literal"><span class="pre">nickname</span></tt>, <tt class="docutils literal"><span class="pre">email</span></tt>, <tt class="docutils literal"><span class="pre">fullname</span></tt>,
+<tt class="docutils literal"><span class="pre">dob</span></tt>, <tt class="docutils literal"><span class="pre">gender</span></tt>, <tt class="docutils literal"><span class="pre">postcode</span></tt>, <tt class="docutils literal"><span class="pre">country</span></tt>, <tt class="docutils literal"><span class="pre">language</span></tt> and <tt class="docutils literal"><span class="pre">timezone</span></tt>.
+Full details are available in the <a class="reference" href="http://openid.net/specs/openid-simple-registration-extension-1_0.html";>spec</a>.</p>
+<p>To request this information, pass the fields that you wish to retrieve as an
+additional <tt class="docutils literal"><span class="pre">sreg</span></tt> argument to the <tt class="docutils literal"><span class="pre">django_openidconsumer.views.begin</span></tt> view:</p>
+<pre class="literal-block">
+(r'^openid/$', 'django_openidconsumer.views.begin', {
+    'sreg': 'email,nickname'
+}),
+</pre>
+<p>Any simple registration fields that are returned will be available in a
+dictionary as the <tt class="docutils literal"><span class="pre">sreg</span></tt> property of the OpenID object:</p>
+<pre class="literal-block">
+def example_sreg(request):
+    if request.openid and request.openid.sreg.has_key('email'):
+        return HttpResponse(&quot;Your e-mail address is: %s&quot; % escape(
+            request.openid.sreg['email']
+        ))
+    else:
+        return HttpResponse(&quot;No e-mail address&quot;)
+</pre>
+</div>
+<div class="section">
+<h2><a id="customisation">Customisation</a></h2>
+<p><tt class="docutils literal"><span class="pre">django_openidconsumer</span></tt> uses two templates:</p>
+<dl class="docutils">
+<dt><tt class="docutils literal"><span class="pre">openid_signin.html</span></tt></dt>
+<dd>The form presented to the user when they sign in.</dd>
+<dt><tt class="docutils literal"><span class="pre">openid_failure.html</span></tt></dt>
+<dd>The template used to display an error message when something goes wrong.</dd>
+</dl>
+<p>You can over-ride the default templates by creating templates of the same name
+and placing them somewhere on your template path. You can find the example
+templates in the <tt class="docutils literal"><span class="pre">django_openidconsumer/templates</span></tt> directory.</p>
+<p>The OpenID specification strongly recommends that any OpenID registration form
+has a <tt class="docutils literal"><span class="pre">name</span></tt> attribute of <tt class="docutils literal"><span class="pre">openid_url</span></tt> to aid browser autocompletion, and
+displays the <a class="reference" href="http://openid.net/login-bg.gif";>OpenID logo</a> inline in the form field using the following CSS:</p>
+<pre class="literal-block">
+input.openid {
+  background: url(/path/to/login-bg.gif) no-repeat;
+  background-position: 0 50%;
+  padding-left: 16px;
+}
+</pre>
+<p>By default, the package expects the <tt class="docutils literal"><span class="pre">django_openidconsumer.views.complete</span></tt>
+view to be located at <tt class="docutils literal"><span class="pre">/openid/complete/</span></tt>. This is the view that the OpenID
+provider will redirect the user to after they have authenticated. If you want to
+put it somewhere else you can either pass an extra <tt class="docutils literal"><span class="pre">redirect_to</span></tt> argument to
+<tt class="docutils literal"><span class="pre">django_openidconsumer.views.begin</span></tt> or add an <tt class="docutils literal"><span class="pre">OPENID_REDIRECT_TO</span></tt> setting
+to <tt class="docutils literal"><span class="pre">settings.py</span></tt>.</p>
+<p>You can pass a <tt class="docutils literal"><span class="pre">?next=</span></tt> query string argument containing a relative URL to
+the <tt class="docutils literal"><span class="pre">begin</span></tt> view to control where the user will be redirected to having
+returned to your site. You can also set the default redirection location
+using the <tt class="docutils literal"><span class="pre">OPENID_REDIRECT_NEXT</span></tt> setting; if you do set set a default the user
+will be redirected to your homepage.</p>
+</div>
+<div class="section">
+<h2><a id="i-names">i-names</a></h2>
+<p><a class="reference" href="http://www.inames.net/";>i-names</a> are part of the OpenID 2.0 specification, which is currently being
+developed. They are supported by the python-openid library, and hence are also
+supported by <tt class="docutils literal"><span class="pre">django_openidconsumer</span></tt>. You can tell if an OpenID is an i-name
+by checking the <tt class="docutils literal"><span class="pre">request.openid.is_iname</span></tt> property.</p>
+<p>If you wish to disable i-name support, you can do so by adding the following to
+your <tt class="docutils literal"><span class="pre">settings.py</span></tt>:</p>
+<pre class="literal-block">
+OPENID_DISALLOW_INAMES = True
+</pre>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/lib/django_openid_auth/openid.txt b/lib/django_openid_auth/openid.txt
new file mode 100644
index 0000000..043cdfe
--- /dev/null
+++ b/lib/django_openid_auth/openid.txt
@@ -0,0 +1,165 @@
+================
+OpenID in Django
+================
+
+The ``django_openidconsumer`` package contains all of the code needed to set up 
+your Django application as an OpenID consumer. You can use it to allow OpenID  
+users to sign in to your site without having to create a new username and 
+password.
+
+Overview
+========
+
+The OpenID consumer system consists of:
+
+    * Views for you to hook in to your application.
+    * Database models implementing the persistence layer of an OpenID consumer.
+    * Middleware that makes ``request.openid`` and ``request.openids`` 
+      properties available to your application views.
+
+Dependencies
+============
+
+``django_openidconsumer`` uses the `python-openid library`_, which must be 
+installed separately somewhere on the Python path. You should install the 1.2.0
+"combo" package which includes the ``yadis`` and ``urljr`` libraries.
+
+The package also depends on the availability of Django's `session support`_.
+
+.. _python-openid library: http://www.openidenabled.com/openid/libraries/python/
+.. _session support: http://www.djangoproject.com/documentation/sessions/
+
+Installation
+============
+
+Having ensured that both the  ``python-openid`` library and the ``django_openidconsumer`` package are available on your Python path, you can 
+add OpenID consumer support to an application by doing the following:
+
+    1. Put ``django_openidconsumer`` in your ``INSTALLED_APPS`` setting.
+    2. Run the command ``manage.py syncdb`` to create the necessary tables.
+    3. Add ``django_openidconsumer.middleware.OpenIDMiddleware`` to your list
+       of ``MIDDLEWARE_CLASSES``, somewhere after the Session middleware.
+    4. Add the following views to your urlconf::
+        
+        (r'^openid/$', 'django_openidconsumer.views.begin'),
+        (r'^openid/complete/$', 'django_openidconsumer.views.complete'),
+        (r'^openid/signout/$', 'django_openidconsumer.views.signout'),
+
+You will then be able to browse to ``example.com/openid/`` and sign in using 
+an OpenID.
+
+Using the OpenID middleware
+===========================
+
+With the Middleware installed, your views will have access to the user's OpenID
+as the ``request.openid`` property. This will be ``None`` if the user has not 
+yet authenticated; otherwise it will be a ``django_openidconsumer.util.OpenID`` 
+instance.
+
+If you want the user's OpenID as a string, call the ``str()`` builtin on the
+OpenID instance::
+
+    def example_view(request):
+        if request.openid:
+            return HttpResponse("OpenID is %s" % escape(str(request.openid)))
+        else:
+            return HttpResponse("No OpenID")
+
+Users can sign in with more than one OpenID. This is supported by the 
+``request.openids`` property, which is a list of ``OpenID`` objects in the order
+in which they were authenticated. ``request.openid`` merely returns the last 
+item in this list.
+
+Using simple registration
+=========================
+
+Simple registration (or `sreg`_) is an extension to the OpenID specification 
+that  allows you to request extra details about a user from their OpenID 
+provider. It is frequently used to pre-populate registration forms with 
+information such as the user's name, e-mail address or date of birth.
+
+.. _sreg: http://openid.net/specs/openid-simple-registration-extension-1_0.html
+
+Be aware that not all OpenID providers support sreg, and there is no guarantee
+that the information you have requested will be returned. Simple registration 
+should be used as a convenience for your users rather than as a required step in
+your authentication process.
+
+Available simple registration fields are ``nickname``, ``email``, ``fullname``,
+``dob``, ``gender``, ``postcode``, ``country``, ``language`` and ``timezone``. 
+Full details are available in the `spec`_.
+
+.. _spec: http://openid.net/specs/openid-simple-registration-extension-1_0.html
+
+To request this information, pass the fields that you wish to retrieve as an 
+additional ``sreg`` argument to the ``django_openidconsumer.views.begin`` view::
+
+    (r'^openid/$', 'django_openidconsumer.views.begin', {
+        'sreg': 'email,nickname'
+    }),
+
+Any simple registration fields that are returned will be available in a 
+dictionary as the ``sreg`` property of the OpenID object::
+
+    def example_sreg(request):
+        if request.openid and request.openid.sreg.has_key('email'):
+            return HttpResponse("Your e-mail address is: %s" % escape(
+                request.openid.sreg['email']
+            ))
+        else:
+            return HttpResponse("No e-mail address")
+
+Customisation
+=============
+
+``django_openidconsumer`` uses two templates:
+
+``openid_signin.html``
+    The form presented to the user when they sign in.
+
+``openid_failure.html``
+    The template used to display an error message when something goes wrong.
+
+You can over-ride the default templates by creating templates of the same name 
+and placing them somewhere on your template path. You can find the example 
+templates in the ``django_openidconsumer/templates`` directory.
+
+The OpenID specification strongly recommends that any OpenID registration form
+has a ``name`` attribute of ``openid_url`` to aid browser autocompletion, and 
+displays the `OpenID logo`_ inline in the form field using the following CSS::
+
+    input.openid {
+      background: url(/path/to/login-bg.gif) no-repeat; 
+      background-position: 0 50%;
+      padding-left: 16px;
+    }
+
+.. _OpenID logo: http://openid.net/login-bg.gif
+
+By default, the package expects the ``django_openidconsumer.views.complete`` 
+view to be located at ``/openid/complete/``. This is the view that the OpenID 
+provider will redirect the user to after they have authenticated. If you want to
+put it somewhere else you can either pass an extra ``redirect_to`` argument to  
+``django_openidconsumer.views.begin`` or add an ``OPENID_REDIRECT_TO`` setting 
+to ``settings.py``.
+
+You can pass a ``?next=`` query string argument containing a relative URL to 
+the ``begin`` view to control where the user will be redirected to having 
+returned to your site. You can also set the default redirection location 
+using the ``OPENID_REDIRECT_NEXT`` setting; if you do set set a default the user
+will be redirected to your homepage.
+
+i-names
+=======
+
+`i-names`_ are part of the OpenID 2.0 specification, which is currently being 
+developed. They are supported by the python-openid library, and hence are also 
+supported by ``django_openidconsumer``. You can tell if an OpenID is an i-name 
+by checking the ``request.openid.is_iname`` property.
+
+.. _i-names: http://www.inames.net/
+
+If you wish to disable i-name support, you can do so by adding the following to
+your ``settings.py``::
+
+    OPENID_DISALLOW_INAMES = True
diff --git a/lib/django_openid_auth/setup.py b/lib/django_openid_auth/setup.py
new file mode 100644
index 0000000..f408343
--- /dev/null
+++ b/lib/django_openid_auth/setup.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+# django-openid-auth -  OpenID integration for django.contrib.auth
+#
+# Copyright (C) 2009-2010 Canonical Ltd.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+"""OpenID integration for django.contrib.auth
+
+A library that can be used to add OpenID support to Django applications.
+The library integrates with Django's built in authentication system, so
+most applications require minimal changes to support OpenID llogin. The
+library also includes the following features:
+  * Basic user details are transferred from the OpenID server via the
+    Simple Registration extension or Attribute Exchange extension.
+  * can be configured to use a fixed OpenID server URL, for use in SSO.
+  * supports the launchpad.net teams extension to get team membership
+    info.
+"""
+
+from distutils.core import setup
+
+
+description, long_description = __doc__.split('\n\n', 1)
+VERSION = '0.4'
+
+setup(
+    name='django-openid-auth',
+    version=VERSION,
+    author='Canonical Ltd',
+    description=description,
+    long_description=long_description,
+    license='BSD',
+    platforms=['any'],
+    url='https://launchpad.net/django-openid-auth',
+    download_url=('http://launchpad.net/django-openid-auth/trunk/%s/+download'
+                  '/django-openid-auth-%s.tar.gz' % (VERSION, VERSION)),
+    classifiers=[
+        'Development Status :: 4 - Beta',
+        'Environment :: Web Environment',
+        'Framework :: Django',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: BSD License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Topic :: Software Development :: Libraries :: Python Modules'
+        ],
+    packages=[
+        'django_openid_auth',
+        'django_openid_auth.management',
+        'django_openid_auth.management.commands',
+        'django_openid_auth.tests',
+        ],
+    package_data={
+        'django_openid_auth': ['templates/openid/*.html'],
+        },
+    provides=['django_openid_auth'],
+    requires=['django (>=1.1.2)', 'openid (>=2.2.0)'],
+    )
diff --git a/lib/django_openid_auth/signals.py b/lib/django_openid_auth/signals.py
new file mode 100644
index 0000000..4cdfdd3
--- /dev/null
+++ b/lib/django_openid_auth/signals.py
@@ -0,0 +1,34 @@
+# django-openid-auth -  OpenID integration for django.contrib.auth
+#
+# Copyright (C) 2007 Simon Willison
+# Copyright (C) 2008-2010 Canonical Ltd.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import django.dispatch
+
+
+openid_login_complete = django.dispatch.Signal(providing_args=[
+    'request', 'openid_response'])
diff --git a/lib/django_openid_auth/tests/test_auth.py b/lib/django_openid_auth/tests/test_auth.py
new file mode 100644
index 0000000..2ef3789
--- /dev/null
+++ b/lib/django_openid_auth/tests/test_auth.py
@@ -0,0 +1,155 @@
+# django-openid-auth -  OpenID integration for django.contrib.auth
+#
+# Copyright (C) 2010 Canonical Ltd.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+
+from django.contrib.auth.models import User
+from django.test import TestCase
+
+from django_openid_auth.auth import OpenIDBackend
+from openid.consumer.consumer import SuccessResponse
+from openid.consumer.discover import OpenIDServiceEndpoint
+from openid.message import Message, OPENID2_NS
+
+
+SREG_NS = "http://openid.net/sreg/1.0";
+AX_NS = "http://openid.net/srv/ax/1.0";
+
+class OpenIDBackendTests(TestCase):
+
+    def setUp(self):
+        super(OpenIDBackendTests, self).setUp()
+        self.backend = OpenIDBackend()
+
+    def test_extract_user_details_sreg(self):
+        endpoint = OpenIDServiceEndpoint()
+        message = Message(OPENID2_NS)
+        message.setArg(SREG_NS, "nickname", "someuser")
+        message.setArg(SREG_NS, "fullname", "Some User")
+        message.setArg(SREG_NS, "email", "foo example com")
+        response = SuccessResponse(
+            endpoint, message, signed_fields=message.toPostArgs().keys())
+
+        data = self.backend._extract_user_details(response)
+        self.assertEqual(data, {"nickname": "someuser",
+                                "first_name": "Some",
+                                "last_name": "User",
+                                "email": "foo example com"})
+
+    def make_response_ax(self, schema="http://axschema.org/";,
+        fullname="Some User", nickname="someuser", email="foo example com",
+        first=None, last=None):
+        endpoint = OpenIDServiceEndpoint()
+        message = Message(OPENID2_NS)
+        attributes = [
+            ("nickname", schema + "namePerson/friendly", nickname),
+            ("fullname", schema + "namePerson", fullname),
+            ("email", schema + "contact/email", email),
+            ]
+        if first:
+            attributes.append(
+                ("first", "http://axschema.org/namePerson/first";, first))
+        if last:
+            attributes.append(
+                ("last", "http://axschema.org/namePerson/last";, last))
+
+        message.setArg(AX_NS, "mode", "fetch_response")
+        for (alias, uri, value) in attributes:
+            message.setArg(AX_NS, "type.%s" % alias, uri)
+            message.setArg(AX_NS, "value.%s" % alias, value)
+        return SuccessResponse(
+            endpoint, message, signed_fields=message.toPostArgs().keys())
+
+    def test_extract_user_details_ax(self):
+        response = self.make_response_ax(fullname="Some User",
+            nickname="someuser", email="foo example com")
+
+        data = self.backend._extract_user_details(response)
+
+        self.assertEqual(data, {"nickname": "someuser",
+                                "first_name": "Some",
+                                "last_name": "User",
+                                "email": "foo example com"})
+
+    def test_extract_user_details_ax_split_name(self):
+        # Include fullname too to show that the split data takes
+        # precedence.
+        response = self.make_response_ax(
+            fullname="Bad Data", first="Some", last="User")
+
+        data = self.backend._extract_user_details(response)
+
+        self.assertEqual(data, {"nickname": "someuser",
+                                "first_name": "Some",
+                                "last_name": "User",
+                                "email": "foo example com"})
+
+    def test_extract_user_details_ax_broken_myopenid(self):
+        response = self.make_response_ax(
+            schema="http://schema.openid.net/";, fullname="Some User",
+            nickname="someuser", email="foo example com")
+
+        data = self.backend._extract_user_details(response)
+
+        self.assertEqual(data, {"nickname": "someuser",
+                                "first_name": "Some",
+                                "last_name": "User",
+                                "email": "foo example com"})
+
+    def test_update_user_details_long_names(self):
+        response = self.make_response_ax()
+        user = User.objects.create_user('someuser', 'someuser example com',
+            password=None)
+        data = dict(first_name=u"Some56789012345678901234567890123",
+            last_name=u"User56789012345678901234567890123",
+            email=u"someotheruser example com")
+
+        self.backend.update_user_details(user, data, response)
+
+        self.assertEqual("Some56789012345678901234567890",  user.first_name)
+        self.assertEqual("User56789012345678901234567890",  user.last_name)
+
+    def test_extract_user_details_name_with_trailing_space(self):
+        response = self.make_response_ax(fullname="SomeUser ")
+
+        data = self.backend._extract_user_details(response)
+
+        self.assertEqual("", data['first_name'])
+        self.assertEqual("SomeUser", data['last_name'])
+
+    def test_extract_user_details_name_with_thin_space(self):
+        response = self.make_response_ax(fullname=u"Some\u2009User")
+
+        data = self.backend._extract_user_details(response)
+
+        self.assertEqual("Some", data['first_name'])
+        self.assertEqual("User", data['last_name'])
+
+
+def suite():
+    return unittest.TestLoader().loadTestsFromName(__name__)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]