Merge lp:~jamesh/django-openid-auth/sanitise-url into lp:~django-openid-auth/django-openid-auth/trunk
- sanitise-url
- Merge into trunk
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 | ||||
Related bugs: |
|
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_
Description of the change
James Henstridge (jamesh) wrote : | # |
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.
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.
Dave Walker (davewalker) wrote : | # |
I can confirm at revision 67, it resolves the issue experienced on bug #10866 on an unrelated project to UbuntuOne.
Martin Albisetti (beuno) wrote : | # |
This fixes the problem in my local instance.
Elliot Murphy (statik) wrote : | # |
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=
--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.
Creating table django_content_type
Processing sessions.Session model
Creating table django_session
Processing admin.LogEntry model
Creating table django_admin_log
Processing django_
Creating table django_
Processing django_
Creating table django_
Processing django_
Creating table django_
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 ...
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.
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.
Martin Albisetti (beuno) wrote : | # |
FWIW, all test seem to pass in karmic
Dave Walker (davewalker) wrote : | # |
It seems to pass on Lucid for me:
http://
dave@micmini:
1.1.1
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-
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://
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 ''.
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.
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_
+ PYTHONPATH=$(shell pwd): python2.5 example_
& to bring deps into path:
~/tmp/sanitise-url$ ln -s /usr/lib/
~/tmp/sanitise-url$ ln -s /usr/lib/
Python2.6 was also lasted updated 5 days ago, with a hefty changelog.
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.
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.
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/
+@@ -624,7 +624,9 @@
+ if type(rawdata) == type(""):
+ self.__
+ else:
+- self.update(
++ # 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?
James Henstridge (jamesh) wrote : | # |
I've summarised the problem here:
http://
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.
Elliot Murphy (statik) wrote : | # |
Thanks for getting this fixed upstream so fast! Also, thanks for filing https:/
Preview Diff
1 | === modified file 'django_openid_auth/__init__.py' | |||
2 | --- django_openid_auth/__init__.py 2010-01-21 21:50:13 +0000 | |||
3 | +++ django_openid_auth/__init__.py 2010-01-25 14:43:11 +0000 | |||
4 | @@ -2,7 +2,6 @@ | |||
5 | 2 | # | 2 | # |
6 | 3 | # Copyright (C) 2007 Simon Willison | 3 | # Copyright (C) 2007 Simon Willison |
7 | 4 | # Copyright (C) 2008-2009 Canonical Ltd. | 4 | # Copyright (C) 2008-2009 Canonical Ltd. |
8 | 5 | # Copyright (C) 2010 Dave Walker | ||
9 | 6 | # | 5 | # |
10 | 7 | # Redistribution and use in source and binary forms, with or without | 6 | # Redistribution and use in source and binary forms, with or without |
11 | 8 | # modification, are permitted provided that the following conditions | 7 | # modification, are permitted provided that the following conditions |
12 | @@ -28,25 +27,3 @@ | |||
13 | 28 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 27 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
14 | 29 | # POSSIBILITY OF SUCH DAMAGE. | 28 | # POSSIBILITY OF SUCH DAMAGE. |
15 | 30 | 29 | ||
16 | 31 | """ Support for allowing openid authentication for /admin (django.contrib.admin) """ | ||
17 | 32 | |||
18 | 33 | from django.conf import settings | ||
19 | 34 | |||
20 | 35 | if getattr(settings, 'OPENID_USE_AS_ADMIN_LOGIN', False): | ||
21 | 36 | from django.http import HttpResponseRedirect | ||
22 | 37 | from django.contrib.admin import sites | ||
23 | 38 | from django_openid_auth import views | ||
24 | 39 | |||
25 | 40 | def _openid_login(self, request, error_message='', extra_context=None): | ||
26 | 41 | if request.user.is_authenticated(): | ||
27 | 42 | if not request.user.is_staff: | ||
28 | 43 | return views.render_failure(request, "User %s does not have admin access." | ||
29 | 44 | % request.user.username) | ||
30 | 45 | return views.render_failure(request, "Unknown Error: %s" % error_message) | ||
31 | 46 | else: | ||
32 | 47 | # Redirect to openid login path, | ||
33 | 48 | return HttpResponseRedirect(settings.LOGIN_URL+"?next="+request.get_full_path()) | ||
34 | 49 | |||
35 | 50 | # Overide the standard admin login form. | ||
36 | 51 | sites.AdminSite.display_login_form = _openid_login | ||
37 | 52 | |||
38 | 53 | 30 | ||
39 | === modified file 'django_openid_auth/admin.py' | |||
40 | --- django_openid_auth/admin.py 2009-04-07 10:26:04 +0000 | |||
41 | +++ django_openid_auth/admin.py 2010-01-25 14:43:11 +0000 | |||
42 | @@ -1,6 +1,7 @@ | |||
43 | 1 | # django-openid-auth - OpenID integration for django.contrib.auth | 1 | # django-openid-auth - OpenID integration for django.contrib.auth |
44 | 2 | # | 2 | # |
45 | 3 | # Copyright (C) 2008-2009 Canonical Ltd. | 3 | # Copyright (C) 2008-2009 Canonical Ltd. |
46 | 4 | # Copyright (C) 2010 Dave Walker | ||
47 | 4 | # | 5 | # |
48 | 5 | # Redistribution and use in source and binary forms, with or without | 6 | # Redistribution and use in source and binary forms, with or without |
49 | 6 | # modification, are permitted provided that the following conditions | 7 | # modification, are permitted provided that the following conditions |
50 | @@ -26,6 +27,7 @@ | |||
51 | 26 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 27 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
52 | 27 | # POSSIBILITY OF SUCH DAMAGE. | 28 | # POSSIBILITY OF SUCH DAMAGE. |
53 | 28 | 29 | ||
54 | 30 | from django.conf import settings | ||
55 | 29 | from django.contrib import admin | 31 | from django.contrib import admin |
56 | 30 | from django_openid_auth.models import Nonce, Association, UserOpenID | 32 | from django_openid_auth.models import Nonce, Association, UserOpenID |
57 | 31 | from django_openid_auth.store import DjangoOpenIDStore | 33 | from django_openid_auth.store import DjangoOpenIDStore |
58 | @@ -64,3 +66,25 @@ | |||
59 | 64 | search_fields = ('claimed_id',) | 66 | search_fields = ('claimed_id',) |
60 | 65 | 67 | ||
61 | 66 | admin.site.register(UserOpenID, UserOpenIDAdmin) | 68 | admin.site.register(UserOpenID, UserOpenIDAdmin) |
62 | 69 | |||
63 | 70 | |||
64 | 71 | # Support for allowing openid authentication for /admin (django.contrib.admin) | ||
65 | 72 | if getattr(settings, 'OPENID_USE_AS_ADMIN_LOGIN', False): | ||
66 | 73 | from django.http import HttpResponseRedirect | ||
67 | 74 | from django_openid_auth import views | ||
68 | 75 | |||
69 | 76 | def _openid_login(self, request, error_message='', extra_context=None): | ||
70 | 77 | if request.user.is_authenticated(): | ||
71 | 78 | if not request.user.is_staff: | ||
72 | 79 | return views.render_failure( | ||
73 | 80 | request, "User %s does not have admin access." | ||
74 | 81 | % request.user.username) | ||
75 | 82 | return views.render_failure( | ||
76 | 83 | request, "Unknown Error: %s" % error_message) | ||
77 | 84 | else: | ||
78 | 85 | # Redirect to openid login path, | ||
79 | 86 | return HttpResponseRedirect( | ||
80 | 87 | settings.LOGIN_URL + "?next=" + request.get_full_path()) | ||
81 | 88 | |||
82 | 89 | # Overide the standard admin login form. | ||
83 | 90 | admin.sites.AdminSite.display_login_form = _openid_login | ||
84 | 67 | 91 | ||
85 | === modified file 'django_openid_auth/forms.py' | |||
86 | --- django_openid_auth/forms.py 2009-06-21 15:44:29 +0000 | |||
87 | +++ django_openid_auth/forms.py 2010-01-25 14:43:11 +0000 | |||
88 | @@ -27,7 +27,6 @@ | |||
89 | 27 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 27 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
90 | 28 | # POSSIBILITY OF SUCH DAMAGE. | 28 | # POSSIBILITY OF SUCH DAMAGE. |
91 | 29 | 29 | ||
92 | 30 | import settings | ||
93 | 31 | from django import forms | 30 | from django import forms |
94 | 32 | from django.contrib.auth.admin import UserAdmin | 31 | from django.contrib.auth.admin import UserAdmin |
95 | 33 | from django.contrib.auth.forms import UserChangeForm | 32 | from django.contrib.auth.forms import UserChangeForm |
96 | 34 | 33 | ||
97 | === modified file 'django_openid_auth/tests/test_views.py' | |||
98 | --- django_openid_auth/tests/test_views.py 2010-01-22 05:14:38 +0000 | |||
99 | +++ django_openid_auth/tests/test_views.py 2010-01-25 14:43:11 +0000 | |||
100 | @@ -126,6 +126,7 @@ | |||
101 | 126 | self.provider = StubOpenIDProvider('http://example.com/') | 126 | self.provider = StubOpenIDProvider('http://example.com/') |
102 | 127 | setDefaultFetcher(self.provider, wrap_exceptions=False) | 127 | setDefaultFetcher(self.provider, wrap_exceptions=False) |
103 | 128 | 128 | ||
104 | 129 | self.old_login_redirect_url = getattr(settings, 'LOGIN_REDIRECT_URL', '/accounts/profile/') | ||
105 | 129 | self.old_create_users = getattr(settings, 'OPENID_CREATE_USERS', False) | 130 | self.old_create_users = getattr(settings, 'OPENID_CREATE_USERS', False) |
106 | 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) |
107 | 131 | self.old_sso_server_url = getattr(settings, 'OPENID_SSO_SERVER_URL') | 132 | self.old_sso_server_url = getattr(settings, 'OPENID_SSO_SERVER_URL') |
108 | @@ -139,6 +140,7 @@ | |||
109 | 139 | settings.OPENID_USE_AS_ADMIN_LOGIN = False | 140 | settings.OPENID_USE_AS_ADMIN_LOGIN = False |
110 | 140 | 141 | ||
111 | 141 | def tearDown(self): | 142 | def tearDown(self): |
112 | 143 | settings.LOGIN_REDIRECT_URL = self.old_login_redirect_url | ||
113 | 142 | settings.OPENID_CREATE_USERS = self.old_create_users | 144 | settings.OPENID_CREATE_USERS = self.old_create_users |
114 | 143 | settings.OPENID_UPDATE_DETAILS_FROM_SREG = self.old_update_details | 145 | settings.OPENID_UPDATE_DETAILS_FROM_SREG = self.old_update_details |
115 | 144 | settings.OPENID_SSO_SERVER_URL = self.old_sso_server_url | 146 | settings.OPENID_SSO_SERVER_URL = self.old_sso_server_url |
116 | @@ -190,6 +192,31 @@ | |||
117 | 190 | response = self.client.get('/getuser/') | 192 | response = self.client.get('/getuser/') |
118 | 191 | self.assertEquals(response.content, 'someuser') | 193 | self.assertEquals(response.content, 'someuser') |
119 | 192 | 194 | ||
120 | 195 | def test_login_no_next(self): | ||
121 | 196 | """Logins with no next parameter redirect to LOGIN_REDIRECT_URL.""" | ||
122 | 197 | user = User.objects.create_user('someuser', 'someone@example.com') | ||
123 | 198 | useropenid = UserOpenID( | ||
124 | 199 | user=user, | ||
125 | 200 | claimed_id='http://example.com/identity', | ||
126 | 201 | display_id='http://example.com/identity') | ||
127 | 202 | useropenid.save() | ||
128 | 203 | |||
129 | 204 | settings.LOGIN_REDIRECT_URL = '/getuser/' | ||
130 | 205 | response = self.client.post('/openid/login/', | ||
131 | 206 | {'openid_identifier': 'http://example.com/identity'}) | ||
132 | 207 | self.assertContains(response, 'OpenID transaction in progress') | ||
133 | 208 | |||
134 | 209 | openid_request = self.provider.parseFormPost(response.content) | ||
135 | 210 | self.assertEquals(openid_request.mode, 'checkid_setup') | ||
136 | 211 | self.assertTrue(openid_request.return_to.startswith( | ||
137 | 212 | 'http://testserver/openid/complete/')) | ||
138 | 213 | |||
139 | 214 | # Complete the request. The user is redirected to the next URL. | ||
140 | 215 | openid_response = openid_request.answer(True) | ||
141 | 216 | response = self.complete(openid_response) | ||
142 | 217 | self.assertRedirects( | ||
143 | 218 | response, 'http://testserver' + settings.LOGIN_REDIRECT_URL) | ||
144 | 219 | |||
145 | 193 | def test_login_sso(self): | 220 | def test_login_sso(self): |
146 | 194 | settings.OPENID_SSO_SERVER_URL = 'http://example.com/identity' | 221 | settings.OPENID_SSO_SERVER_URL = 'http://example.com/identity' |
147 | 195 | user = User.objects.create_user('someuser', 'someone@example.com') | 222 | user = User.objects.create_user('someuser', 'someone@example.com') |
148 | @@ -381,6 +408,9 @@ | |||
149 | 381 | ("http://example.net/foo/bar?baz=quux", False), | 408 | ("http://example.net/foo/bar?baz=quux", False), |
150 | 382 | ("/somewhere/local", True), | 409 | ("/somewhere/local", True), |
151 | 383 | ("/somewhere/local?url=http://fail.com/bar", True), | 410 | ("/somewhere/local?url=http://fail.com/bar", True), |
152 | 411 | # An empty path, as seen when no "next" parameter is passed. | ||
153 | 412 | ("", False), | ||
154 | 413 | ("/path with spaces", False), | ||
155 | 384 | ] | 414 | ] |
156 | 385 | for url, returns_self in urls: | 415 | for url, returns_self in urls: |
157 | 386 | sanitised = sanitise_redirect_url(url) | 416 | sanitised = sanitise_redirect_url(url) |
158 | 387 | 417 | ||
159 | === modified file 'django_openid_auth/views.py' | |||
160 | --- django_openid_auth/views.py 2010-01-14 21:39:55 +0000 | |||
161 | +++ django_openid_auth/views.py 2010-01-25 14:43:11 +0000 | |||
162 | @@ -64,7 +64,10 @@ | |||
163 | 64 | def sanitise_redirect_url(redirect_to): | 64 | def sanitise_redirect_url(redirect_to): |
164 | 65 | """Sanitise the redirection URL.""" | 65 | """Sanitise the redirection URL.""" |
165 | 66 | # Light security check -- make sure redirect_to isn't garbage. | 66 | # Light security check -- make sure redirect_to isn't garbage. |
167 | 67 | if not redirect_to or '//' in redirect_to or ' ' in redirect_to: | 67 | is_valid = True |
168 | 68 | if not redirect_to or ' ' in redirect_to: | ||
169 | 69 | is_valid = False | ||
170 | 70 | elif '//' in redirect_to: | ||
171 | 68 | # Allow the redirect URL to be external if it's a permitted domain | 71 | # Allow the redirect URL to be external if it's a permitted domain |
172 | 69 | allowed_domains = getattr(settings, | 72 | allowed_domains = getattr(settings, |
173 | 70 | "ALLOWED_EXTERNAL_OPENID_REDIRECT_DOMAINS", []) | 73 | "ALLOWED_EXTERNAL_OPENID_REDIRECT_DOMAINS", []) |
174 | @@ -75,11 +78,12 @@ | |||
175 | 75 | if netloc.find(":") != -1: | 78 | if netloc.find(":") != -1: |
176 | 76 | netloc, _ = netloc.split(":", 1) | 79 | netloc, _ = netloc.split(":", 1) |
177 | 77 | if netloc not in allowed_domains: | 80 | if netloc not in allowed_domains: |
183 | 78 | redirect_to = settings.LOGIN_REDIRECT_URL | 81 | is_valid = False |
184 | 79 | else: | 82 | |
185 | 80 | # netloc is blank, so it's a local URL (possibly with another URL | 83 | # If the return_to URL is not valid, use the default. |
186 | 81 | # passed in the querystring. Allow it.) | 84 | if not is_valid: |
187 | 82 | pass | 85 | redirect_to = settings.LOGIN_REDIRECT_URL |
188 | 86 | |||
189 | 83 | return redirect_to | 87 | return redirect_to |
190 | 84 | 88 | ||
191 | 85 | 89 |
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.