Merge lp:~statik/ubuntu/lucid/python-django-openid-auth/new-upstream-version into lp:ubuntu/lucid/python-django-openid-auth

Proposed by Elliot Murphy
Status: Merged
Merged at revision: not available
Proposed branch: lp:~statik/ubuntu/lucid/python-django-openid-auth/new-upstream-version
Merge into: lp:ubuntu/lucid/python-django-openid-auth
Diff against target: 1101 lines (+528/-69)
26 files modified
PKG-INFO (+6/-3)
README.txt (+42/-8)
debian/changelog (+10/-0)
debian/compat (+1/-1)
debian/control (+4/-7)
debian/pycompat (+0/-1)
debian/rules (+2/-3)
debian/source/format (+1/-0)
django_openid_auth/__init__.py (+1/-0)
django_openid_auth/admin.py (+24/-0)
django_openid_auth/auth.py (+9/-0)
django_openid_auth/forms.py (+36/-0)
django_openid_auth/management/__init__.py (+27/-0)
django_openid_auth/management/commands/__init__.py (+27/-0)
django_openid_auth/management/commands/openid_cleanup.py (+28/-0)
django_openid_auth/store.py (+16/-7)
django_openid_auth/templates/openid/login.html (+4/-2)
django_openid_auth/tests/__init__.py (+28/-0)
django_openid_auth/tests/test_store.py (+46/-3)
django_openid_auth/tests/test_views.py (+143/-25)
django_openid_auth/tests/urls.py (+29/-1)
django_openid_auth/urls.py (+3/-3)
django_openid_auth/views.py (+30/-1)
example_consumer/settings.py (+4/-1)
example_consumer/urls.py (+1/-1)
setup.py (+6/-2)
To merge this branch: bzr merge lp:~statik/ubuntu/lucid/python-django-openid-auth/new-upstream-version
Reviewer Review Type Date Requested Status
Daniel Holbach (community) Approve
Ubuntu branches Pending
Review via email: mp+18671@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Elliot Murphy (statik) wrote :

updating to the new upstream release, converting from CDBS to dh7, switching from python-central to python-support, converting to sourcepackage 3.0 (quilt).

Builds and installs ok, confirmed that the upgrade from 0.1 to 0.2 doesn't seem to leave any python-central garbage behind.

Revision history for this message
Elliot Murphy (statik) wrote :

Daniel, I added you as a reviewer only because I know you were anxious to get this new version.

4. By Elliot Murphy

Drop Provides:

Revision history for this message
Daniel Holbach (dholbach) wrote :

Great work!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'PKG-INFO'
--- PKG-INFO 2009-04-08 15:07:49 +0000
+++ PKG-INFO 2010-02-05 06:33:16 +0000
@@ -1,12 +1,12 @@
1Metadata-Version: 1.01Metadata-Version: 1.1
2Name: django-openid-auth2Name: django-openid-auth
3Version: 0.13Version: 0.2
4Summary: OpenID integration for django.contrib.auth4Summary: OpenID integration for django.contrib.auth
5Home-page: https://launchpad.net/django-openid-auth5Home-page: https://launchpad.net/django-openid-auth
6Author: Canonical Ltd6Author: Canonical Ltd
7Author-email: UNKNOWN7Author-email: UNKNOWN
8License: BSD8License: BSD
9Download-URL: https://launchpad.net/django-openid-auth/+download9Download-URL: http://launchpad.net/django-openid-auth/trunk/0.2/+download/django-openid-auth-0.2.tar.gz
10Description: A library that can be used to add OpenID support to Django applications.10Description: A library that can be used to add OpenID support to Django applications.
11 The library integrates with Django's built in authentication system, so11 The library integrates with Django's built in authentication system, so
12 most applications require minimal changes to support OpenID llogin. The12 most applications require minimal changes to support OpenID llogin. The
@@ -26,3 +26,6 @@
26Classifier: Operating System :: OS Independent26Classifier: Operating System :: OS Independent
27Classifier: Programming Language :: Python27Classifier: Programming Language :: Python
28Classifier: Topic :: Software Development :: Libraries :: Python Modules28Classifier: Topic :: Software Development :: Libraries :: Python Modules
29Requires: django (>=1.0)
30Requires: openid (>=2.2.0)
31Provides: django_openid_auth
2932
=== modified file 'README.txt'
--- README.txt 2009-04-08 15:07:49 +0000
+++ README.txt 2010-02-05 06:33:16 +0000
@@ -8,7 +8,14 @@
88
9== Basic Installation ==9== Basic Installation ==
1010
11 1. Add 'django_auth_openid' to INSTALLED_APPS for your application.11 1. Install the Jan Rain Python OpenID library. It can be found at:
12
13 http://openidenabled.com/python-openid/
14
15 It can also be found in most Linux distributions packaged as
16 "python-openid". You will need version 2.2.0 or later.
17
18 2. Add 'django_openid_auth' to INSTALLED_APPS for your application.
12 At a minimum, you'll need the following in there:19 At a minimum, you'll need the following in there:
1320
14 INSTALLED_APPS = (21 INSTALLED_APPS = (
@@ -18,7 +25,7 @@
18 'django_openid_auth',25 'django_openid_auth',
19 )26 )
2027
21 2. Add 'django_auth_openid.auth.OpenIDBackend' to28 3. Add 'django_auth_openid.auth.OpenIDBackend' to
22 AUTHENTICATION_BACKENDS. This should be in addition to the29 AUTHENTICATION_BACKENDS. This should be in addition to the
23 default ModelBackend:30 default ModelBackend:
2431
@@ -27,17 +34,17 @@
27 'django.contrib.auth.backends.ModelBackend',34 'django.contrib.auth.backends.ModelBackend',
28 )35 )
2936
30 3. To create users automatically when a new OpenID is used, add the37 4. To create users automatically when a new OpenID is used, add the
31 following to the settings:38 following to the settings:
3239
33 OPENID_CREATE_USERS = True40 OPENID_CREATE_USERS = True
3441
35 4. To have user details updated from OpenID Simple Registration data42 5. To have user details updated from OpenID Simple Registration data
36 each time they log in, add the following:43 each time they log in, add the following:
3744
38 OPENID_UPDATE_DETAILS_FROM_SREG = True45 OPENID_UPDATE_DETAILS_FROM_SREG = True
3946
40 5. Hook up the login URLs to your application's urlconf with47 6. Hook up the login URLs to your application's urlconf with
41 something like:48 something like:
4249
43 urlpatterns = patterns('',50 urlpatterns = patterns('',
@@ -46,16 +53,16 @@
46 ...53 ...
47 )54 )
4855
49 6. Configure the LOGIN_URL and LOGIN_REDIRECT_URL appropriately for56 7. Configure the LOGIN_URL and LOGIN_REDIRECT_URL appropriately for
50 your site:57 your site:
5158
52 LOGIN_URL = '/openid/login'59 LOGIN_URL = '/openid/login/'
53 LOGIN_REDIRECT_URL = '/'60 LOGIN_REDIRECT_URL = '/'
5461
55 This will allow pages that use the standard @login_required62 This will allow pages that use the standard @login_required
56 decorator to use the OpenID login page.63 decorator to use the OpenID login page.
5764
58 7. Rerun "python manage.py syncdb" to add the UserOpenID table to65 8. Rerun "python manage.py syncdb" to add the UserOpenID table to
59 your database.66 your database.
6067
6168
@@ -90,3 +97,30 @@
9097
91When a user logs in, they will be added or removed from the relevant98When a user logs in, they will be added or removed from the relevant
92teams listed in the mapping.99teams listed in the mapping.
100
101If 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.
102
103 OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO = True
104
105If you use OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO, the variable OPENID_LAUNCHPAD_TEAMS_MAPPING will be ignored.
106If 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.
107
108 OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO_BLACKLIST = ['django-group1', 'django-group2']
109
110== External redirect domains ==
111
112By 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:
113
114 ALLOWED_EXTERNAL_OPENID_REDIRECT_DOMAINS = ['example.com', 'example.org']
115
116and redirects to external URLs on those domains will additionally be permitted.
117
118== Use as /admin (django.admin.contrib) login ==
119
120If you require openid authentication into the admin application, add the following setting:
121
122 OPENID_USE_AS_ADMIN_LOGIN = True
123
124It 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".
125The 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
126openid user to be staff.
93127
=== modified file 'debian/changelog'
--- debian/changelog 2009-04-08 15:07:49 +0000
+++ debian/changelog 2010-02-05 06:33:16 +0000
@@ -1,3 +1,13 @@
1python-django-openid-auth (0.2-0ubuntu1) lucid; urgency=low
2
3 * New upstream release. (LP: #517400)
4 * Switch to dpkg-source 3.0 (quilt) format
5 * Switch to dh 7 from CDBS
6 * Switch from pycentral to pysupport
7 * Bump standards version to 3.8.3
8
9 -- Elliot Murphy <elliot@ubuntu.com> Fri, 05 Feb 2010 00:01:53 -0500
10
1python-django-openid-auth (0.1-0ubuntu1) karmic; urgency=low11python-django-openid-auth (0.1-0ubuntu1) karmic; urgency=low
212
3 * Initial release. (LP: #359304)13 * Initial release. (LP: #359304)
414
=== modified file 'debian/compat'
--- debian/compat 2009-04-08 15:07:49 +0000
+++ debian/compat 2010-02-05 06:33:16 +0000
@@ -1,1 +1,1 @@
1617
22
=== modified file 'debian/control'
--- debian/control 2009-04-08 15:07:49 +0000
+++ debian/control 2010-02-05 06:33:16 +0000
@@ -1,26 +1,23 @@
1Source: python-django-openid-auth1Source: python-django-openid-auth
2Maintainer: Ubuntu MOTU Developers <ubuntu-motu@lists.ubuntu.com>2Maintainer: Ubuntu MOTU Developers <ubuntu-motu@lists.ubuntu.com>
3XSBC-Original-Maintainer: Elliot Murphy <elliot@canonical.com>3XSBC-Original-Maintainer: Elliot Murphy <elliot@ubuntu.com>
4Section: python4Section: python
5Priority: optional5Priority: optional
6Standards-Version: 3.8.06Standards-Version: 3.8.3
7Build-Depends-Indep:7Build-Depends-Indep:
8 python-central (>= 0.6.7)8 python-support (>= 0.6.4)
9Build-Depends:9Build-Depends:
10 cdbs (>= 0.4.51),10 debhelper (>= 7.0.50),
11 debhelper (>= 6.0.4),
12 python (>= 2.5)11 python (>= 2.5)
13XS-Python-Version: all12XS-Python-Version: all
14Homepage: https://launchpad.net/django-openid-auth13Homepage: https://launchpad.net/django-openid-auth
1514
16Package: python-django-openid-auth15Package: python-django-openid-auth
17Architecture: all16Architecture: all
18XB-Python-Version: ${python:Versions}
19Depends: ${python:Depends},17Depends: ${python:Depends},
20 ${misc:Depends},18 ${misc:Depends},
21 python-openid,19 python-openid,
22 python-django20 python-django
23Provides: ${python:Provides}
24Description: OpenID integration for django.contrib.auth21Description: OpenID integration for django.contrib.auth
25 A library that can be used to add OpenID support to Django applications.22 A library that can be used to add OpenID support to Django applications.
26 The library integrates with Django's builtin authentication system, so23 The library integrates with Django's builtin authentication system, so
2724
=== removed file 'debian/pycompat'
--- debian/pycompat 2009-04-08 15:07:49 +0000
+++ debian/pycompat 1970-01-01 00:00:00 +0000
@@ -1,1 +0,0 @@
12
20
=== modified file 'debian/rules'
--- debian/rules 2009-04-08 15:07:49 +0000
+++ debian/rules 2010-02-05 06:33:16 +0000
@@ -1,5 +1,4 @@
1#!/usr/bin/make -f1#!/usr/bin/make -f
22
3include /usr/share/cdbs/1/rules/debhelper.mk3%:
4DEB_PYTHON_SYSTEM = pycentral4 dh --buildsystem=python_distutils $@
5include /usr/share/cdbs/1/class/python-distutils.mk
65
=== added directory 'debian/source'
=== added file 'debian/source/format'
--- debian/source/format 1970-01-01 00:00:00 +0000
+++ debian/source/format 2010-02-05 06:33:16 +0000
@@ -0,0 +1,1 @@
13.0 (quilt)
02
=== modified file 'django_openid_auth/__init__.py'
--- django_openid_auth/__init__.py 2009-04-08 15:07:49 +0000
+++ django_openid_auth/__init__.py 2010-02-05 06:33:16 +0000
@@ -26,3 +26,4 @@
26# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN26# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE27# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28# POSSIBILITY OF SUCH DAMAGE.28# POSSIBILITY OF SUCH DAMAGE.
29
2930
=== modified file 'django_openid_auth/admin.py'
--- django_openid_auth/admin.py 2009-04-08 15:07:49 +0000
+++ django_openid_auth/admin.py 2010-02-05 06:33:16 +0000
@@ -1,6 +1,7 @@
1# django-openid-auth - OpenID integration for django.contrib.auth1# django-openid-auth - OpenID integration for django.contrib.auth
2#2#
3# Copyright (C) 2008-2009 Canonical Ltd.3# Copyright (C) 2008-2009 Canonical Ltd.
4# Copyright (C) 2010 Dave Walker
4#5#
5# Redistribution and use in source and binary forms, with or without6# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions7# modification, are permitted provided that the following conditions
@@ -26,6 +27,7 @@
26# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE27# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27# POSSIBILITY OF SUCH DAMAGE.28# POSSIBILITY OF SUCH DAMAGE.
2829
30from django.conf import settings
29from django.contrib import admin31from django.contrib import admin
30from django_openid_auth.models import Nonce, Association, UserOpenID32from django_openid_auth.models import Nonce, Association, UserOpenID
31from django_openid_auth.store import DjangoOpenIDStore33from django_openid_auth.store import DjangoOpenIDStore
@@ -64,3 +66,25 @@
64 search_fields = ('claimed_id',)66 search_fields = ('claimed_id',)
6567
66admin.site.register(UserOpenID, UserOpenIDAdmin)68admin.site.register(UserOpenID, UserOpenIDAdmin)
69
70
71# Support for allowing openid authentication for /admin (django.contrib.admin)
72if getattr(settings, 'OPENID_USE_AS_ADMIN_LOGIN', False):
73 from django.http import HttpResponseRedirect
74 from django_openid_auth import views
75
76 def _openid_login(self, request, error_message='', extra_context=None):
77 if request.user.is_authenticated():
78 if not request.user.is_staff:
79 return views.render_failure(
80 request, "User %s does not have admin access."
81 % request.user.username)
82 return views.render_failure(
83 request, "Unknown Error: %s" % error_message)
84 else:
85 # Redirect to openid login path,
86 return HttpResponseRedirect(
87 settings.LOGIN_URL + "?next=" + request.get_full_path())
88
89 # Overide the standard admin login form.
90 admin.sites.AdminSite.display_login_form = _openid_login
6791
=== modified file 'django_openid_auth/auth.py'
--- django_openid_auth/auth.py 2009-04-08 15:07:49 +0000
+++ django_openid_auth/auth.py 2010-02-05 06:33:16 +0000
@@ -158,7 +158,16 @@
158 user.save()158 user.save()
159159
160 def update_groups_from_teams(self, user, teams_response):160 def update_groups_from_teams(self, user, teams_response):
161 teams_mapping_auto = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO', False)
162 teams_mapping_auto_blacklist = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO_BLACKLIST', [])
161 teams_mapping = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING', {})163 teams_mapping = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING', {})
164 if teams_mapping_auto:
165 #ignore teams_mapping. use all django-groups
166 teams_mapping = dict()
167 all_groups = Group.objects.exclude(name__in=teams_mapping_auto_blacklist)
168 for group in all_groups:
169 teams_mapping[group.name] = group.name
170
162 if len(teams_mapping) == 0:171 if len(teams_mapping) == 0:
163 return172 return
164173
165174
=== modified file 'django_openid_auth/forms.py'
--- django_openid_auth/forms.py 2009-04-08 15:07:49 +0000
+++ django_openid_auth/forms.py 2010-02-05 06:33:16 +0000
@@ -28,12 +28,48 @@
28# POSSIBILITY OF SUCH DAMAGE.28# POSSIBILITY OF SUCH DAMAGE.
2929
30from django import forms30from django import forms
31from django.contrib.auth.admin import UserAdmin
32from django.contrib.auth.forms import UserChangeForm
33from django.contrib.auth.models import Group
31from django.utils.translation import ugettext as _34from django.utils.translation import ugettext as _
32from django.conf import settings35from django.conf import settings
3336
34from openid.yadis import xri37from openid.yadis import xri
3538
3639
40def teams_new_unicode(self):
41 """
42 Replacement for Group.__unicode__()
43 Calls original method to chain results
44 """
45 name = self.unicode_before_teams()
46 teams_mapping = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING', {})
47 group_teams = [t for t in teams_mapping if teams_mapping[t] == self.name]
48 if len(group_teams) > 0:
49 return "%s -> %s" % (name, ", ".join(group_teams))
50 else:
51 return name
52Group.unicode_before_teams = Group.__unicode__
53Group.__unicode__ = teams_new_unicode
54
55
56class UserChangeFormWithTeamRestriction(UserChangeForm):
57 """
58 Extends UserChangeForm to add teams awareness to the user admin form
59 """
60 def clean_groups(self):
61 data = self.cleaned_data['groups']
62 teams_mapping = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING', {})
63 known_teams = teams_mapping.values()
64 user_groups = self.instance.groups.all()
65 for group in data:
66 if group.name in known_teams and group not in user_groups:
67 raise forms.ValidationError("""The group %s is mapped to an
68 external team. You cannot assign it manually.""" % group.name)
69 return data
70UserAdmin.form = UserChangeFormWithTeamRestriction
71
72
37class OpenIDLoginForm(forms.Form):73class OpenIDLoginForm(forms.Form):
38 openid_identifier = forms.CharField(74 openid_identifier = forms.CharField(
39 max_length=255,75 max_length=255,
4076
=== modified file 'django_openid_auth/management/__init__.py'
--- django_openid_auth/management/__init__.py 2009-04-08 15:07:49 +0000
+++ django_openid_auth/management/__init__.py 2010-02-05 06:33:16 +0000
@@ -0,0 +1,27 @@
1# django-openid-auth - OpenID integration for django.contrib.auth
2#
3# Copyright (C) 2009 Canonical Ltd.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions
7# are met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11#
12# * Redistributions in binary form must reproduce the above copyright
13# notice, this list of conditions and the following disclaimer in the
14# documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27# POSSIBILITY OF SUCH DAMAGE.
028
=== modified file 'django_openid_auth/management/commands/__init__.py'
--- django_openid_auth/management/commands/__init__.py 2009-04-08 15:07:49 +0000
+++ django_openid_auth/management/commands/__init__.py 2010-02-05 06:33:16 +0000
@@ -0,0 +1,27 @@
1# django-openid-auth - OpenID integration for django.contrib.auth
2#
3# Copyright (C) 2009 Canonical Ltd.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions
7# are met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11#
12# * Redistributions in binary form must reproduce the above copyright
13# notice, this list of conditions and the following disclaimer in the
14# documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27# POSSIBILITY OF SUCH DAMAGE.
028
=== modified file 'django_openid_auth/management/commands/openid_cleanup.py'
--- django_openid_auth/management/commands/openid_cleanup.py 2009-04-08 15:07:49 +0000
+++ django_openid_auth/management/commands/openid_cleanup.py 2010-02-05 06:33:16 +0000
@@ -1,3 +1,31 @@
1# django-openid-auth - OpenID integration for django.contrib.auth
2#
3# Copyright (C) 2009 Canonical Ltd.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions
7# are met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11#
12# * Redistributions in binary form must reproduce the above copyright
13# notice, this list of conditions and the following disclaimer in the
14# documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27# POSSIBILITY OF SUCH DAMAGE.
28
1from django.core.management.base import NoArgsCommand29from django.core.management.base import NoArgsCommand
230
3from django_openid_auth.store import DjangoOpenIDStore31from django_openid_auth.store import DjangoOpenIDStore
432
=== modified file 'django_openid_auth/store.py'
--- django_openid_auth/store.py 2009-04-08 15:07:49 +0000
+++ django_openid_auth/store.py 2010-02-05 06:33:16 +0000
@@ -42,13 +42,22 @@
42 self.max_nonce_age = 6 * 60 * 60 # Six hours42 self.max_nonce_age = 6 * 60 * 60 # Six hours
4343
44 def storeAssociation(self, server_url, association):44 def storeAssociation(self, server_url, association):
45 assoc = Association(45 try:
46 server_url=server_url,46 assoc = Association.objects.get(
47 handle=association.handle,47 server_url=server_url, handle=association.handle)
48 secret=base64.encodestring(association.secret),48 except Association.DoesNotExist:
49 issued=association.issued,49 assoc = Association(
50 lifetime=association.lifetime,50 server_url=server_url,
51 assoc_type=association.assoc_type)51 handle=association.handle,
52 secret=base64.encodestring(association.secret),
53 issued=association.issued,
54 lifetime=association.lifetime,
55 assoc_type=association.assoc_type)
56 else:
57 assoc.secret = base64.encodestring(association.secret)
58 assoc.issued = association.issued
59 assoc.lifetime = association.lifetime
60 assoc.assoc_type = association.assoc_type
52 assoc.save()61 assoc.save()
5362
54 def getAssociation(self, server_url, handle=None):63 def getAssociation(self, server_url, handle=None):
5564
=== modified file 'django_openid_auth/templates/openid/login.html'
--- django_openid_auth/templates/openid/login.html 2009-04-08 15:07:49 +0000
+++ django_openid_auth/templates/openid/login.html 2010-02-05 06:33:16 +0000
@@ -28,8 +28,10 @@
28<form name="fopenid" action="{{ action }}" method="post">28<form name="fopenid" action="{{ action }}" method="post">
29 <fieldset>29 <fieldset>
30 <legend>{% trans "Sign In Using Your OpenID" %}</legend>30 <legend>{% trans "Sign In Using Your OpenID" %}</legend>
31 <div class="form-row"><label for="id_openid_identifier">{%31 <div class="form-row">
32 trans "OpenID:" %}</label><br />{{ form.openid_identifier }}</div>32 <label for="id_openid_identifier">{% trans "OpenID:" %}</label><br />
33 {{ form.openid_identifier }}
34 </div>
33 <div class="submit-row "><input name="bsignin" type="submit" value="{% trans "Sign in" %}"></div>35 <div class="submit-row "><input name="bsignin" type="submit" value="{% trans "Sign in" %}"></div>
3436
35 {% if next %}37 {% if next %}
3638
=== modified file 'django_openid_auth/tests/__init__.py'
--- django_openid_auth/tests/__init__.py 2009-04-08 15:07:49 +0000
+++ django_openid_auth/tests/__init__.py 2010-02-05 06:33:16 +0000
@@ -1,3 +1,31 @@
1# django-openid-auth - OpenID integration for django.contrib.auth
2#
3# Copyright (C) 2009 Canonical Ltd.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions
7# are met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11#
12# * Redistributions in binary form must reproduce the above copyright
13# notice, this list of conditions and the following disclaimer in the
14# documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27# POSSIBILITY OF SUCH DAMAGE.
28
1import unittest29import unittest
230
331
432
=== modified file 'django_openid_auth/tests/test_store.py'
--- django_openid_auth/tests/test_store.py 2009-04-08 15:07:49 +0000
+++ django_openid_auth/tests/test_store.py 2010-02-05 06:33:16 +0000
@@ -1,3 +1,31 @@
1# django-openid-auth - OpenID integration for django.contrib.auth
2#
3# Copyright (C) 2009 Canonical Ltd.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions
7# are met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11#
12# * Redistributions in binary form must reproduce the above copyright
13# notice, this list of conditions and the following disclaimer in the
14# documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27# POSSIBILITY OF SUCH DAMAGE.
28
1import time29import time
2import unittest30import unittest
331
@@ -28,6 +56,20 @@
28 self.assertEquals(dbassoc.lifetime, 600)56 self.assertEquals(dbassoc.lifetime, 600)
29 self.assertEquals(dbassoc.assoc_type, 'HMAC-SHA1')57 self.assertEquals(dbassoc.assoc_type, 'HMAC-SHA1')
3058
59 def test_storeAssociation_update_existing(self):
60 assoc = OIDAssociation('handle', 'secret', 42, 600, 'HMAC-SHA1')
61 self.store.storeAssociation('server-url', assoc)
62
63 # Now update the association with new information.
64 assoc = OIDAssociation('handle', 'secret2', 420, 900, 'HMAC-SHA256')
65 self.store.storeAssociation('server-url', assoc)
66 dbassoc = Association.objects.get(
67 server_url='server-url', handle='handle')
68 self.assertEqual(dbassoc.secret, 'secret2'.encode('base-64'))
69 self.assertEqual(dbassoc.issued, 420)
70 self.assertEqual(dbassoc.lifetime, 900)
71 self.assertEqual(dbassoc.assoc_type, 'HMAC-SHA256')
72
31 def test_getAssociation(self):73 def test_getAssociation(self):
32 timestamp = int(time.time())74 timestamp = int(time.time())
33 self.store.storeAssociation(75 self.store.storeAssociation(
@@ -76,9 +118,6 @@
76 self.assertEquals(assoc.issued, timestamp + 1)118 self.assertEquals(assoc.issued, timestamp + 1)
77119
78 def test_removeAssociation(self):120 def test_removeAssociation(self):
79 self.assertEquals(
80 self.store.removeAssociation('server-url', 'unknown'), False)
81
82 timestamp = int(time.time())121 timestamp = int(time.time())
83 self.store.storeAssociation(122 self.store.storeAssociation(
84 'server-url', OIDAssociation('handle', 'secret', timestamp, 600,123 'server-url', OIDAssociation('handle', 'secret', timestamp, 600,
@@ -88,6 +127,10 @@
88 self.assertEquals(127 self.assertEquals(
89 self.store.getAssociation('server-url', 'handle'), None)128 self.store.getAssociation('server-url', 'handle'), None)
90129
130 def test_removeAssociation_unknown(self):
131 self.assertEquals(
132 self.store.removeAssociation('server-url', 'unknown'), False)
133
91 def test_useNonce(self):134 def test_useNonce(self):
92 timestamp = time.time()135 timestamp = time.time()
93 # The nonce can only be used once.136 # The nonce can only be used once.
94137
=== modified file 'django_openid_auth/tests/test_views.py'
--- django_openid_auth/tests/test_views.py 2009-04-08 15:07:49 +0000
+++ django_openid_auth/tests/test_views.py 2010-02-05 06:33:16 +0000
@@ -1,3 +1,31 @@
1# django-openid-auth - OpenID integration for django.contrib.auth
2#
3# Copyright (C) 2009 Canonical Ltd.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions
7# are met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11#
12# * Redistributions in binary form must reproduce the above copyright
13# notice, this list of conditions and the following disclaimer in the
14# documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27# POSSIBILITY OF SUCH DAMAGE.
28
1import cgi29import cgi
2import re30import re
3import time31import time
@@ -15,6 +43,7 @@
1543
16from django_openid_auth import teams44from django_openid_auth import teams
17from django_openid_auth.models import UserOpenID45from django_openid_auth.models import UserOpenID
46from django_openid_auth.views import sanitise_redirect_url
1847
1948
20ET = importElementTree()49ET = importElementTree()
@@ -97,21 +126,26 @@
97 self.provider = StubOpenIDProvider('http://example.com/')126 self.provider = StubOpenIDProvider('http://example.com/')
98 setDefaultFetcher(self.provider, wrap_exceptions=False)127 setDefaultFetcher(self.provider, wrap_exceptions=False)
99128
129 self.old_login_redirect_url = getattr(settings, 'LOGIN_REDIRECT_URL', '/accounts/profile/')
100 self.old_create_users = getattr(settings, 'OPENID_CREATE_USERS', False)130 self.old_create_users = getattr(settings, 'OPENID_CREATE_USERS', False)
101 self.old_update_details = getattr(settings, 'OPENID_UPDATE_DETAILS_FROM_SREG', False)131 self.old_update_details = getattr(settings, 'OPENID_UPDATE_DETAILS_FROM_SREG', False)
102 self.old_sso_server_url = getattr(settings, 'OPENID_SSO_SERVER_URL')132 self.old_sso_server_url = getattr(settings, 'OPENID_SSO_SERVER_URL')
103 self.old_teams_map = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING', {})133 self.old_teams_map = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING', {})
134 self.old_use_as_admin_login = getattr(settings, 'OPENID_USE_AS_ADMIN_LOGIN', False)
104135
105 settings.OPENID_CREATE_USERS = False136 settings.OPENID_CREATE_USERS = False
106 settings.OPENID_UPDATE_DETAILS_FROM_SREG = False137 settings.OPENID_UPDATE_DETAILS_FROM_SREG = False
107 settings.OPENID_SSO_SERVER_URL = None138 settings.OPENID_SSO_SERVER_URL = None
108 settings.OPENID_LAUNCHPAD_TEAMS_MAPPING = {}139 settings.OPENID_LAUNCHPAD_TEAMS_MAPPING = {}
140 settings.OPENID_USE_AS_ADMIN_LOGIN = False
109141
110 def tearDown(self):142 def tearDown(self):
143 settings.LOGIN_REDIRECT_URL = self.old_login_redirect_url
111 settings.OPENID_CREATE_USERS = self.old_create_users144 settings.OPENID_CREATE_USERS = self.old_create_users
112 settings.OPENID_UPDATE_DETAILS_FROM_SREG = self.old_update_details145 settings.OPENID_UPDATE_DETAILS_FROM_SREG = self.old_update_details
113 settings.OPENID_SSO_SERVER_URL = self.old_sso_server_url146 settings.OPENID_SSO_SERVER_URL = self.old_sso_server_url
114 settings.OPENID_LAUNCHPAD_TEAMS_MAPPING = self.old_teams_map147 settings.OPENID_LAUNCHPAD_TEAMS_MAPPING = self.old_teams_map
148 settings.OPENID_USE_AS_ADMIN_LOGIN = self.old_use_as_admin_login
115149
116 setDefaultFetcher(None)150 setDefaultFetcher(None)
117 super(RelyingPartyTests, self).tearDown()151 super(RelyingPartyTests, self).tearDown()
@@ -122,8 +156,8 @@
122 self.assertEquals(webresponse.code, 302)156 self.assertEquals(webresponse.code, 302)
123 redirect_to = webresponse.headers['location']157 redirect_to = webresponse.headers['location']
124 self.assertTrue(redirect_to.startswith(158 self.assertTrue(redirect_to.startswith(
125 'http://testserver/openid/complete'))159 'http://testserver/openid/complete/'))
126 return self.client.get('/openid/complete',160 return self.client.get('/openid/complete/',
127 dict(cgi.parse_qsl(redirect_to.split('?', 1)[1])))161 dict(cgi.parse_qsl(redirect_to.split('?', 1)[1])))
128162
129 def test_login(self):163 def test_login(self):
@@ -135,29 +169,54 @@
135 useropenid.save()169 useropenid.save()
136170
137 # The login form is displayed:171 # The login form is displayed:
138 response = self.client.get('/openid/login')172 response = self.client.get('/openid/login/')
139 self.assertTemplateUsed(response, 'openid/login.html')173 self.assertTemplateUsed(response, 'openid/login.html')
140174
141 # Posting in an identity URL begins the authentication request:175 # Posting in an identity URL begins the authentication request:
142 response = self.client.post('/openid/login',176 response = self.client.post('/openid/login/',
143 {'openid_identifier': 'http://example.com/identity',177 {'openid_identifier': 'http://example.com/identity',
144 'next': '/getuser'})178 'next': '/getuser/'})
145 self.assertContains(response, 'OpenID transaction in progress')179 self.assertContains(response, 'OpenID transaction in progress')
146180
147 openid_request = self.provider.parseFormPost(response.content)181 openid_request = self.provider.parseFormPost(response.content)
148 self.assertEquals(openid_request.mode, 'checkid_setup')182 self.assertEquals(openid_request.mode, 'checkid_setup')
149 self.assertTrue(openid_request.return_to.startswith(183 self.assertTrue(openid_request.return_to.startswith(
150 'http://testserver/openid/complete'))184 'http://testserver/openid/complete/'))
151185
152 # Complete the request. The user is redirected to the next URL.186 # Complete the request. The user is redirected to the next URL.
153 openid_response = openid_request.answer(True)187 openid_response = openid_request.answer(True)
154 response = self.complete(openid_response)188 response = self.complete(openid_response)
155 self.assertRedirects(response, 'http://testserver/getuser')189 self.assertRedirects(response, 'http://testserver/getuser/')
156190
157 # And they are now logged in:191 # And they are now logged in:
158 response = self.client.get('/getuser')192 response = self.client.get('/getuser/')
159 self.assertEquals(response.content, 'someuser')193 self.assertEquals(response.content, 'someuser')
160194
195 def test_login_no_next(self):
196 """Logins with no next parameter redirect to LOGIN_REDIRECT_URL."""
197 user = User.objects.create_user('someuser', 'someone@example.com')
198 useropenid = UserOpenID(
199 user=user,
200 claimed_id='http://example.com/identity',
201 display_id='http://example.com/identity')
202 useropenid.save()
203
204 settings.LOGIN_REDIRECT_URL = '/getuser/'
205 response = self.client.post('/openid/login/',
206 {'openid_identifier': 'http://example.com/identity'})
207 self.assertContains(response, 'OpenID transaction in progress')
208
209 openid_request = self.provider.parseFormPost(response.content)
210 self.assertEquals(openid_request.mode, 'checkid_setup')
211 self.assertTrue(openid_request.return_to.startswith(
212 'http://testserver/openid/complete/'))
213
214 # Complete the request. The user is redirected to the next URL.
215 openid_response = openid_request.answer(True)
216 response = self.complete(openid_response)
217 self.assertRedirects(
218 response, 'http://testserver' + settings.LOGIN_REDIRECT_URL)
219
161 def test_login_sso(self):220 def test_login_sso(self):
162 settings.OPENID_SSO_SERVER_URL = 'http://example.com/identity'221 settings.OPENID_SSO_SERVER_URL = 'http://example.com/identity'
163 user = User.objects.create_user('someuser', 'someone@example.com')222 user = User.objects.create_user('someuser', 'someone@example.com')
@@ -169,22 +228,22 @@
169228
170 # Requesting the login form immediately begins an229 # Requesting the login form immediately begins an
171 # authentication request.230 # authentication request.
172 response = self.client.get('/openid/login', {'next': '/getuser'})231 response = self.client.get('/openid/login/', {'next': '/getuser/'})
173 self.assertEquals(response.status_code, 200)232 self.assertEquals(response.status_code, 200)
174 self.assertContains(response, 'OpenID transaction in progress')233 self.assertContains(response, 'OpenID transaction in progress')
175234
176 openid_request = self.provider.parseFormPost(response.content)235 openid_request = self.provider.parseFormPost(response.content)
177 self.assertEquals(openid_request.mode, 'checkid_setup')236 self.assertEquals(openid_request.mode, 'checkid_setup')
178 self.assertTrue(openid_request.return_to.startswith(237 self.assertTrue(openid_request.return_to.startswith(
179 'http://testserver/openid/complete'))238 'http://testserver/openid/complete/'))
180239
181 # Complete the request. The user is redirected to the next URL.240 # Complete the request. The user is redirected to the next URL.
182 openid_response = openid_request.answer(True)241 openid_response = openid_request.answer(True)
183 response = self.complete(openid_response)242 response = self.complete(openid_response)
184 self.assertRedirects(response, 'http://testserver/getuser')243 self.assertRedirects(response, 'http://testserver/getuser/')
185244
186 # And they are now logged in:245 # And they are now logged in:
187 response = self.client.get('/getuser')246 response = self.client.get('/getuser/')
188 self.assertEquals(response.content, 'someuser')247 self.assertEquals(response.content, 'someuser')
189248
190 def test_login_create_users(self):249 def test_login_create_users(self):
@@ -193,9 +252,9 @@
193 User.objects.create_user('someuser', 'someone@example.com')252 User.objects.create_user('someuser', 'someone@example.com')
194253
195 # Posting in an identity URL begins the authentication request:254 # Posting in an identity URL begins the authentication request:
196 response = self.client.post('/openid/login',255 response = self.client.post('/openid/login/',
197 {'openid_identifier': 'http://example.com/identity',256 {'openid_identifier': 'http://example.com/identity',
198 'next': '/getuser'})257 'next': '/getuser/'})
199 self.assertContains(response, 'OpenID transaction in progress')258 self.assertContains(response, 'OpenID transaction in progress')
200259
201 # Complete the request, passing back some simple registration260 # Complete the request, passing back some simple registration
@@ -208,11 +267,11 @@
208 'email': 'foo@example.com'})267 'email': 'foo@example.com'})
209 openid_response.addExtension(sreg_response)268 openid_response.addExtension(sreg_response)
210 response = self.complete(openid_response)269 response = self.complete(openid_response)
211 self.assertRedirects(response, 'http://testserver/getuser')270 self.assertRedirects(response, 'http://testserver/getuser/')
212271
213 # And they are now logged in as a new user (they haven't taken272 # And they are now logged in as a new user (they haven't taken
214 # over the existing "someuser" user).273 # over the existing "someuser" user).
215 response = self.client.get('/getuser')274 response = self.client.get('/getuser/')
216 self.assertEquals(response.content, 'someuser2')275 self.assertEquals(response.content, 'someuser2')
217276
218 # Check the details of the new user.277 # Check the details of the new user.
@@ -231,9 +290,9 @@
231 useropenid.save()290 useropenid.save()
232291
233 # Posting in an identity URL begins the authentication request:292 # Posting in an identity URL begins the authentication request:
234 response = self.client.post('/openid/login',293 response = self.client.post('/openid/login/',
235 {'openid_identifier': 'http://example.com/identity',294 {'openid_identifier': 'http://example.com/identity',
236 'next': '/getuser'})295 'next': '/getuser/'})
237 self.assertContains(response, 'OpenID transaction in progress')296 self.assertContains(response, 'OpenID transaction in progress')
238297
239 # Complete the request, passing back some simple registration298 # Complete the request, passing back some simple registration
@@ -246,11 +305,11 @@
246 'email': 'foo@example.com'})305 'email': 'foo@example.com'})
247 openid_response.addExtension(sreg_response)306 openid_response.addExtension(sreg_response)
248 response = self.complete(openid_response)307 response = self.complete(openid_response)
249 self.assertRedirects(response, 'http://testserver/getuser')308 self.assertRedirects(response, 'http://testserver/getuser/')
250309
251 # And they are now logged in as testuser (the passed in310 # And they are now logged in as testuser (the passed in
252 # nickname has not caused the username to change).311 # nickname has not caused the username to change).
253 response = self.client.get('/getuser')312 response = self.client.get('/getuser/')
254 self.assertEquals(response.content, 'testuser')313 self.assertEquals(response.content, 'testuser')
255314
256 # The user's full name and email have been updated.315 # The user's full name and email have been updated.
@@ -276,9 +335,9 @@
276 useropenid.save()335 useropenid.save()
277336
278 # Posting in an identity URL begins the authentication request:337 # Posting in an identity URL begins the authentication request:
279 response = self.client.post('/openid/login',338 response = self.client.post('/openid/login/',
280 {'openid_identifier': 'http://example.com/identity',339 {'openid_identifier': 'http://example.com/identity',
281 'next': '/getuser'})340 'next': '/getuser/'})
282 self.assertContains(response, 'OpenID transaction in progress')341 self.assertContains(response, 'OpenID transaction in progress')
283342
284 # Complete the request343 # Complete the request
@@ -289,10 +348,10 @@
289 teams_request, 'teamname,some-other-team')348 teams_request, 'teamname,some-other-team')
290 openid_response.addExtension(teams_response)349 openid_response.addExtension(teams_response)
291 response = self.complete(openid_response)350 response = self.complete(openid_response)
292 self.assertRedirects(response, 'http://testserver/getuser')351 self.assertRedirects(response, 'http://testserver/getuser/')
293352
294 # And they are now logged in as testuser353 # And they are now logged in as testuser
295 response = self.client.get('/getuser')354 response = self.client.get('/getuser/')
296 self.assertEquals(response.content, 'testuser')355 self.assertEquals(response.content, 'testuser')
297356
298 # The user's groups have been updated.357 # The user's groups have been updated.
@@ -300,6 +359,65 @@
300 self.assertTrue(group in user.groups.all())359 self.assertTrue(group in user.groups.all())
301 self.assertTrue(ogroup not in user.groups.all())360 self.assertTrue(ogroup not in user.groups.all())
302361
303362 def test_login_teams_automapping(self):
363 settings.OPENID_LAUNCHPAD_TEAMS_MAPPING = {'teamname': 'groupname',
364 'otherteam': 'othergroup'}
365 settings.OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO = True
366 settings.OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO_BLACKLIST = ['django-group1', 'django-group2']
367 user = User.objects.create_user('testuser', 'someone@example.com')
368 group1 = Group(name='django-group1')
369 group1.save()
370 group2 = Group(name='django-group2')
371 group2.save()
372 group3 = Group(name='django-group3')
373 group3.save()
374 user.save()
375 useropenid = UserOpenID(
376 user=user,
377 claimed_id='http://example.com/identity',
378 display_id='http://example.com/identity')
379 useropenid.save()
380
381 # Posting in an identity URL begins the authentication request:
382 response = self.client.post('/openid/login/',
383 {'openid_identifier': 'http://example.com/identity',
384 'next': '/getuser/'})
385 self.assertContains(response, 'OpenID transaction in progress')
386
387 # Complete the request
388 openid_request = self.provider.parseFormPost(response.content)
389 openid_response = openid_request.answer(True)
390 teams_request = teams.TeamsRequest.fromOpenIDRequest(openid_request)
391
392 self.assertEqual(group1 in user.groups.all(), False)
393 self.assertEqual(group2 in user.groups.all(), False)
394 self.assertTrue(group3 not in user.groups.all())
395
396class HelperFunctionsTest(TestCase):
397 def test_sanitise_redirect_url(self):
398 settings.ALLOWED_EXTERNAL_OPENID_REDIRECT_DOMAINS = [
399 "example.com", "example.org"]
400 # list of URLs and whether they should be passed or not
401 urls = [
402 ("http://example.com", True),
403 ("http://example.org/", True),
404 ("http://example.org/foo/bar", True),
405 ("http://example.org/foo/bar?baz=quux", True),
406 ("http://example.org:9999/foo/bar?baz=quux", True),
407 ("http://www.example.org/", False),
408 ("http://example.net/foo/bar?baz=quux", False),
409 ("/somewhere/local", True),
410 ("/somewhere/local?url=http://fail.com/bar", True),
411 # An empty path, as seen when no "next" parameter is passed.
412 ("", False),
413 ("/path with spaces", False),
414 ]
415 for url, returns_self in urls:
416 sanitised = sanitise_redirect_url(url)
417 if returns_self:
418 self.assertEqual(url, sanitised)
419 else:
420 self.assertEqual(settings.LOGIN_REDIRECT_URL, sanitised)
421
304def suite():422def suite():
305 return unittest.TestLoader().loadTestsFromName(__name__)423 return unittest.TestLoader().loadTestsFromName(__name__)
306424
=== modified file 'django_openid_auth/tests/urls.py'
--- django_openid_auth/tests/urls.py 2009-04-08 15:07:49 +0000
+++ django_openid_auth/tests/urls.py 2010-02-05 06:33:16 +0000
@@ -1,3 +1,31 @@
1# django-openid-auth - OpenID integration for django.contrib.auth
2#
3# Copyright (C) 2009 Canonical Ltd.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions
7# are met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11#
12# * Redistributions in binary form must reproduce the above copyright
13# notice, this list of conditions and the following disclaimer in the
14# documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27# POSSIBILITY OF SUCH DAMAGE.
28
1from django.http import HttpResponse29from django.http import HttpResponse
2from django.conf.urls.defaults import *30from django.conf.urls.defaults import *
331
@@ -6,6 +34,6 @@
6 return HttpResponse(request.user.username)34 return HttpResponse(request.user.username)
735
8urlpatterns = patterns('',36urlpatterns = patterns('',
9 (r'^getuser', get_user),37 (r'^getuser/$', get_user),
10 (r'^openid/', include('django_openid_auth.urls')),38 (r'^openid/', include('django_openid_auth.urls')),
11)39)
1240
=== modified file 'django_openid_auth/urls.py'
--- django_openid_auth/urls.py 2009-04-08 15:07:49 +0000
+++ django_openid_auth/urls.py 2010-02-05 06:33:16 +0000
@@ -30,7 +30,7 @@
30from django.conf.urls.defaults import *30from django.conf.urls.defaults import *
3131
32urlpatterns = patterns('django_openid_auth.views',32urlpatterns = patterns('django_openid_auth.views',
33 (r'^login$', 'login_begin'),33 (r'^login/$', 'login_begin'),
34 (r'^complete$', 'login_complete'),34 (r'^complete/$', 'login_complete'),
35 url(r'^logo$', 'logo', name='openid-logo'),35 url(r'^logo.gif$', 'logo', name='openid-logo'),
36)36)
3737
=== modified file 'django_openid_auth/views.py'
--- django_openid_auth/views.py 2009-04-08 15:07:49 +0000
+++ django_openid_auth/views.py 2010-02-05 06:33:16 +0000
@@ -29,10 +29,12 @@
2929
30import re30import re
31import urllib31import urllib
32from urlparse import urlsplit
3233
33from django.conf import settings34from django.conf import settings
34from django.contrib.auth import (35from django.contrib.auth import (
35 REDIRECT_FIELD_NAME, authenticate, login as auth_login)36 REDIRECT_FIELD_NAME, authenticate, login as auth_login)
37from django.contrib.auth.models import Group
36from django.core.urlresolvers import reverse38from django.core.urlresolvers import reverse
37from django.http import HttpResponse, HttpResponseRedirect39from django.http import HttpResponse, HttpResponseRedirect
38from django.shortcuts import render_to_response40from django.shortcuts import render_to_response
@@ -62,8 +64,26 @@
62def sanitise_redirect_url(redirect_to):64def sanitise_redirect_url(redirect_to):
63 """Sanitise the redirection URL."""65 """Sanitise the redirection URL."""
64 # Light security check -- make sure redirect_to isn't garbage.66 # Light security check -- make sure redirect_to isn't garbage.
65 if not redirect_to or '//' in redirect_to or ' ' in redirect_to:67 is_valid = True
68 if not redirect_to or ' ' in redirect_to:
69 is_valid = False
70 elif '//' in redirect_to:
71 # Allow the redirect URL to be external if it's a permitted domain
72 allowed_domains = getattr(settings,
73 "ALLOWED_EXTERNAL_OPENID_REDIRECT_DOMAINS", [])
74 s, netloc, p, q, f = urlsplit(redirect_to)
75 # allow it if netloc is blank or if the domain is allowed
76 if netloc:
77 # a domain was specified. Is it an allowed domain?
78 if netloc.find(":") != -1:
79 netloc, _ = netloc.split(":", 1)
80 if netloc not in allowed_domains:
81 is_valid = False
82
83 # If the return_to URL is not valid, use the default.
84 if not is_valid:
66 redirect_to = settings.LOGIN_REDIRECT_URL85 redirect_to = settings.LOGIN_REDIRECT_URL
86
67 return redirect_to87 return redirect_to
6888
6989
@@ -148,7 +168,16 @@
148 sreg.SRegRequest(optional=['email', 'fullname', 'nickname']))168 sreg.SRegRequest(optional=['email', 'fullname', 'nickname']))
149169
150 # Request team info170 # Request team info
171 teams_mapping_auto = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO', False)
172 teams_mapping_auto_blacklist = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO_BLACKLIST', [])
151 launchpad_teams = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING', {})173 launchpad_teams = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING', {})
174 if teams_mapping_auto:
175 #ignore launchpad teams. use all django-groups
176 launchpad_teams = dict()
177 all_groups = Group.objects.exclude(name__in=teams_mapping_auto_blacklist)
178 for group in all_groups:
179 launchpad_teams[group.name] = group.name
180
152 if launchpad_teams:181 if launchpad_teams:
153 openid_request.addExtension(teams.TeamsRequest(launchpad_teams.keys()))182 openid_request.addExtension(teams.TeamsRequest(launchpad_teams.keys()))
154183
155184
=== modified file 'example_consumer/settings.py'
--- example_consumer/settings.py 2009-04-08 15:07:49 +0000
+++ example_consumer/settings.py 2010-02-05 06:33:16 +0000
@@ -125,5 +125,8 @@
125OPENID_SSO_SERVER_URL = 'https://login.launchpad.net/'125OPENID_SSO_SERVER_URL = 'https://login.launchpad.net/'
126126
127# Tell django.contrib.auth to use the OpenID signin URLs.127# Tell django.contrib.auth to use the OpenID signin URLs.
128LOGIN_URL = '/openid/login'128LOGIN_URL = '/openid/login/'
129LOGIN_REDIRECT_URL = '/'129LOGIN_REDIRECT_URL = '/'
130
131# Should django_auth_openid be used to sign into the admin interface?
132OPENID_USE_AS_ADMIN_LOGIN = False
130133
=== modified file 'example_consumer/urls.py'
--- example_consumer/urls.py 2009-04-08 15:07:49 +0000
+++ example_consumer/urls.py 2010-02-05 06:33:16 +0000
@@ -38,7 +38,7 @@
38urlpatterns = patterns('',38urlpatterns = patterns('',
39 (r'^$', views.index),39 (r'^$', views.index),
40 (r'^openid/', include('django_openid_auth.urls')),40 (r'^openid/', include('django_openid_auth.urls')),
41 (r'^logout$', 'django.contrib.auth.views.logout'),41 (r'^logout/$', 'django.contrib.auth.views.logout'),
42 (r'^private/$', views.require_authentication),42 (r'^private/$', views.require_authentication),
4343
44 (r'^admin/(.*)', admin.site.root),44 (r'^admin/(.*)', admin.site.root),
4545
=== modified file 'setup.py'
--- setup.py 2009-04-08 15:07:49 +0000
+++ setup.py 2010-02-05 06:33:16 +0000
@@ -43,17 +43,19 @@
4343
4444
45description, long_description = __doc__.split('\n\n', 1)45description, long_description = __doc__.split('\n\n', 1)
46VERSION = '0.2'
4647
47setup(48setup(
48 name='django-openid-auth',49 name='django-openid-auth',
49 version='0.1',50 version=VERSION,
50 author='Canonical Ltd',51 author='Canonical Ltd',
51 description=description,52 description=description,
52 long_description=long_description,53 long_description=long_description,
53 license='BSD',54 license='BSD',
54 platforms=['any'],55 platforms=['any'],
55 url='https://launchpad.net/django-openid-auth',56 url='https://launchpad.net/django-openid-auth',
56 download_url='https://launchpad.net/django-openid-auth/+download',57 download_url=('http://launchpad.net/django-openid-auth/trunk/%s/+download'
58 '/django-openid-auth-%s.tar.gz' % (VERSION, VERSION)),
57 classifiers=[59 classifiers=[
58 'Development Status :: 4 - Beta',60 'Development Status :: 4 - Beta',
59 'Environment :: Web Environment',61 'Environment :: Web Environment',
@@ -73,4 +75,6 @@
73 package_data={75 package_data={
74 'django_openid_auth': ['templates/openid/*.html'],76 'django_openid_auth': ['templates/openid/*.html'],
75 },77 },
78 provides=['django_openid_auth'],
79 requires=['django (>=1.0)', 'openid (>=2.2.0)'],
76 )80 )

Subscribers

People subscribed via source and target branches

to all changes: