Merge lp:~jamesh/django-openid-auth/sanitise-url into lp:~django-openid-auth/django-openid-auth/trunk

Proposed by James Henstridge
Status: Merged
Approved by: James Henstridge
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~jamesh/django-openid-auth/sanitise-url
Merge into: lp:~django-openid-auth/django-openid-auth/trunk
Diff against target: 189 lines (+64/-30)
5 files modified
django_openid_auth/__init__.py (+0/-23)
django_openid_auth/admin.py (+24/-0)
django_openid_auth/forms.py (+0/-1)
django_openid_auth/tests/test_views.py (+30/-0)
django_openid_auth/views.py (+10/-6)
To merge this branch: bzr merge lp:~jamesh/django-openid-auth/sanitise-url
Reviewer Review Type Date Requested Status
Martin Albisetti (community) Approve
John O'Brien (community) Approve
Review via email: mp+17997@code.launchpad.net

Commit message

Make sanitise_redirect_url map the empty string to LOGIN_REDIRECT_URL. Fixes bug #510866.

To post a comment you must log in.
Revision history for this message
James Henstridge (jamesh) wrote :

Fix the sanitise_redirect_url() function to convert the empty string into settings.LOGIN_REDIRECT_URL again. Without this, the login_complete() view redirects back to itself if no "next" parameter was given.

Revision history for this message
John O'Brien (jdobrien) wrote :

I would like to approve this, however I can't test it as there are a lot of unrelated errors when testing it with our code.

Revision history for this message
John O'Brien (jdobrien) wrote :

This got it working and users experiencing the error no longer get it with this branch. Thank you.

Thanks for fixing the bad import of forms.py too.

review: Approve
Revision history for this message
Dave Walker (davewalker) wrote :

I can confirm at revision 67, it resolves the issue experienced on bug #10866 on an unrelated project to UbuntuOne.

Revision history for this message
Martin Albisetti (beuno) wrote :

This fixes the problem in my local instance.

review: Approve
Revision history for this message
Elliot Murphy (statik) wrote :
Download full text (24.6 KiB)

The attempt to merge lp:~jamesh/django-openid-auth/sanitise-url into lp:django-openid-auth failed.Below is the output from the failed tests.

PYTHONPATH=/tmp/django-openid-auth python example_consumer/manage.py test \
    --verbosity=2 django_openid_auth
Creating test database...
Processing auth.Permission model
Creating table auth_permission
Processing auth.Group model
Creating table auth_group
Processing auth.User model
Creating table auth_user
Processing auth.Message model
Creating table auth_message
Processing contenttypes.ContentType model
Creating table django_content_type
Processing sessions.Session model
Creating table django_session
Processing admin.LogEntry model
Creating table django_admin_log
Processing django_openid_auth.Nonce model
Creating table django_openid_auth_nonce
Processing django_openid_auth.Association model
Creating table django_openid_auth_association
Processing django_openid_auth.UserOpenID model
Creating table django_openid_auth_useropenid
Creating many-to-many tables for auth.Group model
Creating many-to-many tables for auth.User model
Running post-sync handlers for application auth
Adding permission 'auth | permission | Can add permission'
Adding permission 'auth | permission | Can change permission'
Adding permission 'auth | permission | Can delete permission'
Adding permission 'auth | group | Can add group'
Adding permission 'auth | group | Can change group'
Adding permission 'auth | group | Can delete group'
Adding permission 'auth | user | Can add user'
Adding permission 'auth | user | Can change user'
Adding permission 'auth | user | Can delete user'
Adding permission 'auth | message | Can add message'
Adding permission 'auth | message | Can change message'
Adding permission 'auth | message | Can delete message'
Running post-sync handlers for application contenttypes
Adding permission 'contenttypes | content type | Can add content type'
Adding permission 'contenttypes | content type | Can change content type'
Adding permission 'contenttypes | content type | Can delete content type'
Running post-sync handlers for application sessions
Adding permission 'sessions | session | Can add session'
Adding permission 'sessions | session | Can change session'
Adding permission 'sessions | session | Can delete session'
Running post-sync handlers for application admin
Adding permission 'admin | log entry | Can add log entry'
Adding permission 'admin | log entry | Can change log entry'
Adding permission 'admin | log entry | Can delete log entry'
Running post-sync handlers for application django_openid_auth
Adding permission 'django_openid_auth | nonce | Can add nonce'
Adding permission 'django_openid_auth | nonce | Can change nonce'
Adding permission 'django_openid_auth | nonce | Can delete nonce'
Adding permission 'django_openid_auth | association | Can add association'
Adding permission 'django_openid_auth | association | Can change association'
Adding permission 'django_openid_auth | association | Can delete association'
Adding permission 'django_openid_auth | user open id | Can add user open id'
Adding permission 'django_openid_auth | user open id | Can change user open id'
Adding permission 'django_openid_auth | user ...

Revision history for this message
Nicola Larosa (teknico) wrote :

Using this, I'm getting the same behavior as with rev.36 of django-openid-auth, that is, the "hola" user gets logged in, but has no subscription, and only sees the "Account" tab.

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

make check passed on my jaunty machine. it fails on my lucid machine. I've tried downgrading python-django to 1.0.2-1ubuntu0.1, and I still get the failures on my lucid machine.

Revision history for this message
Martin Albisetti (beuno) wrote :

FWIW, all test seem to pass in karmic

Revision history for this message
Dave Walker (davewalker) wrote :

It seems to pass on Lucid for me:
http://pastebin.com/f55390dc2

dave@micmini:~/tmp/sanitise-url$ django-admin --version
1.1.1

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

thanks for trying that out dave! i am at a loss as to why it's continuing to fail on my box. i'm firing up a testdrive-select-iso instance of lucid right now to test it there and see if i can figure anything out.

Revision history for this message
Dave Walker (davewalker) wrote :

My lucid box was around a week out of date, i've just upgraded and run the tests again.. and it did indeed fail. This indicates that something has recently changed, causing these issues outside of django-openid-auth.

make check log:
http://pastebin.com/f3179579a

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

I tried this again tonight with a brand new pristine install of lucid and got the same 5 tests failing. I wonder what has changed in lucid to cause the currently logged in user to be ''.

Revision history for this message
Dave Walker (davewalker) wrote :

It's interesting that python-openid in Lucid was last sync'd 5 days ago. Whilst it seems to be the identical version as Karmic, and the sole difference being promotion to main, it does interest me why the unittests seemed to work on Lucid(-week of update), and fails now.

Revision history for this message
Dave Walker (davewalker) wrote :

Running the tests on Lucid under python2.6 fail. Running under python2.5 passes.

I ran this testing doing:

=== modified file 'Makefile'
 check:
- PYTHONPATH=$(shell pwd) python example_consumer/manage.py test \
+ PYTHONPATH=$(shell pwd): python2.5 example_consumer/manage.py test \

& to bring deps into path:

~/tmp/sanitise-url$ ln -s /usr/lib/pymodules/python2.6/django .
~/tmp/sanitise-url$ ln -s /usr/lib/pymodules/python2.6/openid .

Python2.6 was also lasted updated 5 days ago, with a hefty changelog.

Revision history for this message
James Henstridge (jamesh) wrote :

I'm upgrading my laptop to Lucid to try and reproduce the bug.

As far as the bug goes, the view that is being rendered by the test prints response.user.username. If we aren't authenticated, then request.user will be an AnonymousUser instance, whose username attribute is the empty string. If this is a real bug rather than a quirk of the test environment, then it is a serious bug.

That said, it is a bug that is orthogonal to this merge proposal. Given that it does pass on Karmic, I think merging should be fine.

Revision history for this message
James Henstridge (jamesh) wrote :

This appears to be the change in the python2.6 package causing the failures:

+--- Lib/Cookie.py (.../tags/r264) (Revision 77683)
++++ Lib/Cookie.py (.../branches/release26-maint) (Revision 77683)
+@@ -624,7 +624,9 @@
+ if type(rawdata) == type(""):
+ self.__ParseString(rawdata)
+ else:
+- self.update(rawdata)
++ # self.update() wouldn't call our custom __setitem__
++ for k, v in rawdata.items():
++ self[k] = v
+ return
+ # end load()
+

Putting a copy of Cookie.py with that change backed out on the python path makes the tests pass. The code appears to function correctly with the standard Cookie.py, so I wonder if the bug is inside the Django test infrastructure?

Revision history for this message
James Henstridge (jamesh) wrote :

I've summarised the problem here:

http://code.djangoproject.com/ticket/12720

As it is definitely a Django bug (or a Python bug if the Django developers can convince the Python developers ...), I'm going to merge this.

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

Thanks for getting this fixed upstream so fast! Also, thanks for filing https://bugs.edge.launchpad.net/django/+bug/513719 to track the fix going into Lucid.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'django_openid_auth/__init__.py'
--- django_openid_auth/__init__.py 2010-01-21 21:50:13 +0000
+++ django_openid_auth/__init__.py 2010-01-25 14:43:11 +0000
@@ -2,7 +2,6 @@
2#2#
3# Copyright (C) 2007 Simon Willison3# Copyright (C) 2007 Simon Willison
4# Copyright (C) 2008-2009 Canonical Ltd.4# Copyright (C) 2008-2009 Canonical Ltd.
5# Copyright (C) 2010 Dave Walker
6#5#
7# Redistribution and use in source and binary forms, with or without6# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions7# modification, are permitted provided that the following conditions
@@ -28,25 +27,3 @@
28# 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
29# POSSIBILITY OF SUCH DAMAGE.28# POSSIBILITY OF SUCH DAMAGE.
3029
31""" Support for allowing openid authentication for /admin (django.contrib.admin) """
32
33from django.conf import settings
34
35if getattr(settings, 'OPENID_USE_AS_ADMIN_LOGIN', False):
36 from django.http import HttpResponseRedirect
37 from django.contrib.admin import sites
38 from django_openid_auth import views
39
40 def _openid_login(self, request, error_message='', extra_context=None):
41 if request.user.is_authenticated():
42 if not request.user.is_staff:
43 return views.render_failure(request, "User %s does not have admin access."
44 % request.user.username)
45 return views.render_failure(request, "Unknown Error: %s" % error_message)
46 else:
47 # Redirect to openid login path,
48 return HttpResponseRedirect(settings.LOGIN_URL+"?next="+request.get_full_path())
49
50 # Overide the standard admin login form.
51 sites.AdminSite.display_login_form = _openid_login
52
5330
=== modified file 'django_openid_auth/admin.py'
--- django_openid_auth/admin.py 2009-04-07 10:26:04 +0000
+++ django_openid_auth/admin.py 2010-01-25 14:43:11 +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/forms.py'
--- django_openid_auth/forms.py 2009-06-21 15:44:29 +0000
+++ django_openid_auth/forms.py 2010-01-25 14:43:11 +0000
@@ -27,7 +27,6 @@
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.
2929
30import settings
31from django import forms30from django import forms
32from django.contrib.auth.admin import UserAdmin31from django.contrib.auth.admin import UserAdmin
33from django.contrib.auth.forms import UserChangeForm32from django.contrib.auth.forms import UserChangeForm
3433
=== modified file 'django_openid_auth/tests/test_views.py'
--- django_openid_auth/tests/test_views.py 2010-01-22 05:14:38 +0000
+++ django_openid_auth/tests/test_views.py 2010-01-25 14:43:11 +0000
@@ -126,6 +126,7 @@
126 self.provider = StubOpenIDProvider('http://example.com/')126 self.provider = StubOpenIDProvider('http://example.com/')
127 setDefaultFetcher(self.provider, wrap_exceptions=False)127 setDefaultFetcher(self.provider, wrap_exceptions=False)
128128
129 self.old_login_redirect_url = getattr(settings, 'LOGIN_REDIRECT_URL', '/accounts/profile/')
129 self.old_create_users = getattr(settings, 'OPENID_CREATE_USERS', False)130 self.old_create_users = getattr(settings, 'OPENID_CREATE_USERS', False)
130 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)
131 self.old_sso_server_url = getattr(settings, 'OPENID_SSO_SERVER_URL')132 self.old_sso_server_url = getattr(settings, 'OPENID_SSO_SERVER_URL')
@@ -139,6 +140,7 @@
139 settings.OPENID_USE_AS_ADMIN_LOGIN = False140 settings.OPENID_USE_AS_ADMIN_LOGIN = False
140141
141 def tearDown(self):142 def tearDown(self):
143 settings.LOGIN_REDIRECT_URL = self.old_login_redirect_url
142 settings.OPENID_CREATE_USERS = self.old_create_users144 settings.OPENID_CREATE_USERS = self.old_create_users
143 settings.OPENID_UPDATE_DETAILS_FROM_SREG = self.old_update_details145 settings.OPENID_UPDATE_DETAILS_FROM_SREG = self.old_update_details
144 settings.OPENID_SSO_SERVER_URL = self.old_sso_server_url146 settings.OPENID_SSO_SERVER_URL = self.old_sso_server_url
@@ -190,6 +192,31 @@
190 response = self.client.get('/getuser/')192 response = self.client.get('/getuser/')
191 self.assertEquals(response.content, 'someuser')193 self.assertEquals(response.content, 'someuser')
192194
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
193 def test_login_sso(self):220 def test_login_sso(self):
194 settings.OPENID_SSO_SERVER_URL = 'http://example.com/identity'221 settings.OPENID_SSO_SERVER_URL = 'http://example.com/identity'
195 user = User.objects.create_user('someuser', 'someone@example.com')222 user = User.objects.create_user('someuser', 'someone@example.com')
@@ -381,6 +408,9 @@
381 ("http://example.net/foo/bar?baz=quux", False),408 ("http://example.net/foo/bar?baz=quux", False),
382 ("/somewhere/local", True),409 ("/somewhere/local", True),
383 ("/somewhere/local?url=http://fail.com/bar", 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),
384 ]414 ]
385 for url, returns_self in urls:415 for url, returns_self in urls:
386 sanitised = sanitise_redirect_url(url)416 sanitised = sanitise_redirect_url(url)
387417
=== modified file 'django_openid_auth/views.py'
--- django_openid_auth/views.py 2010-01-14 21:39:55 +0000
+++ django_openid_auth/views.py 2010-01-25 14:43:11 +0000
@@ -64,7 +64,10 @@
64def sanitise_redirect_url(redirect_to):64def sanitise_redirect_url(redirect_to):
65 """Sanitise the redirection URL."""65 """Sanitise the redirection URL."""
66 # Light security check -- make sure redirect_to isn't garbage.66 # Light security check -- make sure redirect_to isn't garbage.
67 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:
68 # Allow the redirect URL to be external if it's a permitted domain71 # Allow the redirect URL to be external if it's a permitted domain
69 allowed_domains = getattr(settings, 72 allowed_domains = getattr(settings,
70 "ALLOWED_EXTERNAL_OPENID_REDIRECT_DOMAINS", [])73 "ALLOWED_EXTERNAL_OPENID_REDIRECT_DOMAINS", [])
@@ -75,11 +78,12 @@
75 if netloc.find(":") != -1:78 if netloc.find(":") != -1:
76 netloc, _ = netloc.split(":", 1)79 netloc, _ = netloc.split(":", 1)
77 if netloc not in allowed_domains:80 if netloc not in allowed_domains:
78 redirect_to = settings.LOGIN_REDIRECT_URL81 is_valid = False
79 else:82
80 # netloc is blank, so it's a local URL (possibly with another URL83 # If the return_to URL is not valid, use the default.
81 # passed in the querystring. Allow it.)84 if not is_valid:
82 pass85 redirect_to = settings.LOGIN_REDIRECT_URL
86
83 return redirect_to87 return redirect_to
8488
8589

Subscribers

People subscribed via source and target branches