Merge lp:~gary/launchpad/loggerheadlogout into lp:launchpad
- loggerheadlogout
- Merge into devel
Proposed by
Gary Poster
Status: | Merged |
---|---|
Approved by: | Gary Poster |
Approved revision: | no longer in the source branch. |
Merged at revision: | 10855 |
Proposed branch: | lp:~gary/launchpad/loggerheadlogout |
Merge into: | lp:launchpad |
Diff against target: |
408 lines (+258/-13) 8 files modified
buildout-templates/bin/test.in (+1/-1) lib/canonical/launchpad/tests/test_login.py (+60/-2) lib/canonical/launchpad/webapp/login.py (+5/-4) lib/canonical/launchpad/webapp/tests/no-anonymous-session-cookies.txt (+14/-3) lib/launchpad_loggerhead/app.py (+15/-1) lib/launchpad_loggerhead/session.py (+9/-2) lib/launchpad_loggerhead/tests.py (+146/-0) lib/lp/testopenid/browser/server.py (+8/-0) |
To merge this branch: | bzr merge lp:~gary/launchpad/loggerheadlogout |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paul Hummer (community) | Approve | ||
Review via email: mp+25108@code.launchpad.net |
Commit message
log out from bzr and openid after logging out from Launchpad.
Description of the change
This is a merge of changes from a branch that's been applied to production. Please see the previous MP for details:
https:/
Thank you
Gary
To post a comment you must log in.
Revision history for this message
Paul Hummer (rockstar) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'buildout-templates/bin/test.in' | |||
2 | --- buildout-templates/bin/test.in 2010-04-26 16:00:31 +0000 | |||
3 | +++ buildout-templates/bin/test.in 2010-05-11 22:19:24 +0000 | |||
4 | @@ -162,7 +162,7 @@ | |||
5 | 162 | # Find tests in the tests and ftests directories | 162 | # Find tests in the tests and ftests directories |
6 | 163 | 'tests_pattern': '^f?tests$', | 163 | 'tests_pattern': '^f?tests$', |
7 | 164 | 'test_path': [${buildout:directory/lib|path-repr}], | 164 | 'test_path': [${buildout:directory/lib|path-repr}], |
9 | 165 | 'package': ['canonical', 'lp', 'devscripts'], | 165 | 'package': ['canonical', 'lp', 'devscripts', 'launchpad_loggerhead'], |
10 | 166 | 'layer': ['!(MailmanLayer)'], | 166 | 'layer': ['!(MailmanLayer)'], |
11 | 167 | } | 167 | } |
12 | 168 | 168 | ||
13 | 169 | 169 | ||
14 | === modified file 'lib/canonical/launchpad/tests/test_login.py' | |||
15 | --- lib/canonical/launchpad/tests/test_login.py 2010-03-30 20:02:53 +0000 | |||
16 | +++ lib/canonical/launchpad/tests/test_login.py 2010-05-11 22:19:24 +0000 | |||
17 | @@ -1,9 +1,11 @@ | |||
19 | 1 | # Copyright 2009 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2010 Canonical Ltd. This software is licensed under the |
20 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
21 | 3 | 3 | ||
22 | 4 | import cgi | ||
23 | 4 | from datetime import datetime | 5 | from datetime import datetime |
24 | 5 | import unittest | 6 | import unittest |
25 | 6 | 7 | ||
26 | 8 | import lazr.uri | ||
27 | 7 | from zope.component import getUtility | 9 | from zope.component import getUtility |
28 | 8 | from zope.event import notify | 10 | from zope.event import notify |
29 | 9 | from zope.session.interfaces import ISession | 11 | from zope.session.interfaces import ISession |
30 | @@ -17,7 +19,8 @@ | |||
31 | 17 | from canonical.launchpad.webapp.authentication import LaunchpadPrincipal | 19 | from canonical.launchpad.webapp.authentication import LaunchpadPrincipal |
32 | 18 | from canonical.launchpad.webapp.interfaces import ( | 20 | from canonical.launchpad.webapp.interfaces import ( |
33 | 19 | CookieAuthLoggedInEvent, ILaunchpadPrincipal, IPlacelessAuthUtility) | 21 | CookieAuthLoggedInEvent, ILaunchpadPrincipal, IPlacelessAuthUtility) |
35 | 20 | from canonical.launchpad.webapp.login import logInPrincipal, logoutPerson | 22 | from canonical.launchpad.webapp.login import ( |
36 | 23 | CookieLogoutPage, logInPrincipal, logoutPerson) | ||
37 | 21 | from canonical.launchpad.webapp.servers import LaunchpadTestRequest | 24 | from canonical.launchpad.webapp.servers import LaunchpadTestRequest |
38 | 22 | from canonical.testing import DatabaseFunctionalLayer | 25 | from canonical.testing import DatabaseFunctionalLayer |
39 | 23 | 26 | ||
40 | @@ -71,6 +74,61 @@ | |||
41 | 71 | self.request) | 74 | self.request) |
42 | 72 | self.failUnless(principal is None) | 75 | self.failUnless(principal is None) |
43 | 73 | 76 | ||
44 | 77 | def test_CookieLogoutPage(self): | ||
45 | 78 | # This test shows that the CookieLogoutPage redirects as we expect: | ||
46 | 79 | # first to loggerhead for it to log out (see bug 574493) and then | ||
47 | 80 | # to our OpenId provider for it to log out (see bug 568106). This | ||
48 | 81 | # will need to be readdressed when we want to accept other OpenId | ||
49 | 82 | # providers, unfortunately. | ||
50 | 83 | |||
51 | 84 | # This is to setup an interaction so that we can call logInPrincipal | ||
52 | 85 | # below. | ||
53 | 86 | login('foo.bar@example.com') | ||
54 | 87 | |||
55 | 88 | logInPrincipal(self.request, self.principal, 'foo.bar@example.com') | ||
56 | 89 | |||
57 | 90 | # Normally CookieLogoutPage is magically mixed in with a base class | ||
58 | 91 | # that accepts context and request and sets up other things. We're | ||
59 | 92 | # just going to put the request on the base class ourselves for this | ||
60 | 93 | # test. | ||
61 | 94 | |||
62 | 95 | view = CookieLogoutPage() | ||
63 | 96 | view.request = self.request | ||
64 | 97 | |||
65 | 98 | # We need to set the session cookie so it can be expired. | ||
66 | 99 | self.request.response.setCookie( | ||
67 | 100 | config.launchpad_session.cookie, 'xxx') | ||
68 | 101 | |||
69 | 102 | # Now we logout. | ||
70 | 103 | |||
71 | 104 | result = view.logout() | ||
72 | 105 | |||
73 | 106 | # We should, in fact, be logged out (this calls logoutPerson). | ||
74 | 107 | |||
75 | 108 | principal = getUtility(IPlacelessAuthUtility).authenticate( | ||
76 | 109 | self.request) | ||
77 | 110 | self.failUnless(principal is None) | ||
78 | 111 | |||
79 | 112 | # The view should have redirected us, with no actual response body. | ||
80 | 113 | |||
81 | 114 | self.assertEquals(self.request.response.getStatus(), 302) | ||
82 | 115 | self.assertEquals(result, '') | ||
83 | 116 | |||
84 | 117 | # We are redirecting to Loggerhead, to ask it to logout. | ||
85 | 118 | |||
86 | 119 | location = lazr.uri.URI(self.request.response.getHeader('location')) | ||
87 | 120 | self.assertEquals(location.host, 'bazaar.launchpad.dev') | ||
88 | 121 | self.assertEquals(location.scheme, 'https') | ||
89 | 122 | self.assertEquals(location.path, '/+logout') | ||
90 | 123 | |||
91 | 124 | # That page should then redirect to our OpenId provider to logout, | ||
92 | 125 | # which we provide in our query string. See | ||
93 | 126 | # launchpad_loggerhead.tests.TestLogout for the pertinent tests. | ||
94 | 127 | |||
95 | 128 | query = cgi.parse_qs(location.query) | ||
96 | 129 | self.assertEquals( | ||
97 | 130 | query['next_to'][0], 'http://testopenid.dev/+logout') | ||
98 | 131 | |||
99 | 74 | def test_logging_in_and_logging_out_the_old_way(self): | 132 | def test_logging_in_and_logging_out_the_old_way(self): |
100 | 75 | # A test showing that we can authenticate a request that had the | 133 | # A test showing that we can authenticate a request that had the |
101 | 76 | # person/account ID stored in the 'personid' session variable instead | 134 | # person/account ID stored in the 'personid' session variable instead |
102 | 77 | 135 | ||
103 | === modified file 'lib/canonical/launchpad/webapp/login.py' | |||
104 | --- lib/canonical/launchpad/webapp/login.py 2010-04-27 18:48:31 +0000 | |||
105 | +++ lib/canonical/launchpad/webapp/login.py 2010-05-11 22:19:24 +0000 | |||
106 | @@ -502,10 +502,11 @@ | |||
107 | 502 | 502 | ||
108 | 503 | def logout(self): | 503 | def logout(self): |
109 | 504 | logoutPerson(self.request) | 504 | logoutPerson(self.request) |
114 | 505 | self.request.response.addNoticeNotification( | 505 | openid_vhost = config.launchpad.openid_provider_vhost |
115 | 506 | _(u'You have been logged out') | 506 | openid_root = allvhosts.configs[openid_vhost].rooturl |
116 | 507 | ) | 507 | target = '%s+logout?%s' % ( |
117 | 508 | target = '%s/?loggingout=1' % self.request.URL[-1] | 508 | config.codehosting.secure_codebrowse_root, |
118 | 509 | urllib.urlencode(dict(next_to='%s+logout' % (openid_root,)))) | ||
119 | 509 | self.request.response.redirect(target) | 510 | self.request.response.redirect(target) |
120 | 510 | return '' | 511 | return '' |
121 | 511 | 512 | ||
122 | 512 | 513 | ||
123 | === modified file 'lib/canonical/launchpad/webapp/tests/no-anonymous-session-cookies.txt' | |||
124 | --- lib/canonical/launchpad/webapp/tests/no-anonymous-session-cookies.txt 2010-02-25 10:50:31 +0000 | |||
125 | +++ lib/canonical/launchpad/webapp/tests/no-anonymous-session-cookies.txt 2010-05-11 22:19:24 +0000 | |||
126 | @@ -1,5 +1,5 @@ | |||
127 | 1 | We will verify that we do not put session cookies in anonymous requests. This | 1 | We will verify that we do not put session cookies in anonymous requests. This |
129 | 2 | is important for cacheing anonymous requests in front of Zope, such as with | 2 | is important for caching anonymous requests in front of Zope, such as with |
130 | 3 | Squid. Note that we are checking whether the browser has a session cookie | 3 | Squid. Note that we are checking whether the browser has a session cookie |
131 | 4 | set, not whether the server has sent a "set-cookie" header. | 4 | set, not whether the server has sent a "set-cookie" header. |
132 | 5 | 5 | ||
133 | @@ -49,8 +49,19 @@ | |||
134 | 49 | minute time interval (set in canonical.launchpad.webapp.login and enforced | 49 | minute time interval (set in canonical.launchpad.webapp.login and enforced |
135 | 50 | with an assert in canonical.launchpad.webapp.session) is intended to be fudge | 50 | with an assert in canonical.launchpad.webapp.session) is intended to be fudge |
136 | 51 | time for browsers with bad system clocks. | 51 | time for browsers with bad system clocks. |
139 | 52 | 52 | >>> # XXX 2010-05-08 bac bug=577596 This work-around for the fact | |
140 | 53 | >>> browser.getControl('Log Out').click() | 53 | >>> # that loggerhead is not running needs to be replaced with |
141 | 54 | >>> # something more robust and clear. | ||
142 | 55 | >>> from urllib2 import HTTPError, URLError | ||
143 | 56 | >>> try: | ||
144 | 57 | ... browser.getControl('Log Out').click() | ||
145 | 58 | ... except (HTTPError, URLError): | ||
146 | 59 | ... pass | ||
147 | 60 | |||
148 | 61 | After ensuring the browser has not left the launchpad.dev domain, the | ||
149 | 62 | single cookie is shown to have the ten minute expiration. | ||
150 | 63 | |||
151 | 64 | >>> browser.open('http://launchpad.dev:8085') | ||
152 | 54 | >>> len(browser.cookies) | 65 | >>> len(browser.cookies) |
153 | 55 | 1 | 66 | 1 |
154 | 56 | >>> expires = browser.cookies.getinfo('launchpad_tests')['expires'] | 67 | >>> expires = browser.cookies.getinfo('launchpad_tests')['expires'] |
155 | 57 | 68 | ||
156 | === modified file 'lib/launchpad_loggerhead/app.py' | |||
157 | --- lib/launchpad_loggerhead/app.py 2010-05-04 23:42:28 +0000 | |||
158 | +++ lib/launchpad_loggerhead/app.py 2010-05-11 22:19:24 +0000 | |||
159 | @@ -123,7 +123,7 @@ | |||
160 | 123 | elif response.status == CANCEL: | 123 | elif response.status == CANCEL: |
161 | 124 | self.log.error('open id response: CANCEL') | 124 | self.log.error('open id response: CANCEL') |
162 | 125 | exc = HTTPUnauthorized() | 125 | exc = HTTPUnauthorized() |
164 | 126 | exc.explanation = "Authetication cancelled." | 126 | exc.explanation = "Authentication cancelled." |
165 | 127 | raise exc | 127 | raise exc |
166 | 128 | else: | 128 | else: |
167 | 129 | self.log.error('open id response: UNKNOWN') | 129 | self.log.error('open id response: UNKNOWN') |
168 | @@ -131,6 +131,18 @@ | |||
169 | 131 | exc.explanation = "Unknown OpenID response." | 131 | exc.explanation = "Unknown OpenID response." |
170 | 132 | raise exc | 132 | raise exc |
171 | 133 | 133 | ||
172 | 134 | def _logout(self, environ, start_response): | ||
173 | 135 | """Logout of loggerhead. | ||
174 | 136 | |||
175 | 137 | Clear the cookie and redirect to `next_to`. | ||
176 | 138 | """ | ||
177 | 139 | environ[self.session_var].clear() | ||
178 | 140 | query = dict(parse_querystring(environ)) | ||
179 | 141 | next_url = query.get('next_to') | ||
180 | 142 | if next_url is None: | ||
181 | 143 | next_url = allvhosts.configs['mainsite'].rooturl | ||
182 | 144 | raise HTTPMovedPermanently(next_url) | ||
183 | 145 | |||
184 | 134 | def __call__(self, environ, start_response): | 146 | def __call__(self, environ, start_response): |
185 | 135 | environ['loggerhead.static.url'] = environ['SCRIPT_NAME'] | 147 | environ['loggerhead.static.url'] = environ['SCRIPT_NAME'] |
186 | 136 | if environ['PATH_INFO'].startswith('/static/'): | 148 | if environ['PATH_INFO'].startswith('/static/'): |
187 | @@ -142,6 +154,8 @@ | |||
188 | 142 | return robots_app(environ, start_response) | 154 | return robots_app(environ, start_response) |
189 | 143 | elif environ['PATH_INFO'].startswith('/+login'): | 155 | elif environ['PATH_INFO'].startswith('/+login'): |
190 | 144 | return self._complete_login(environ, start_response) | 156 | return self._complete_login(environ, start_response) |
191 | 157 | elif environ['PATH_INFO'].startswith('/+logout'): | ||
192 | 158 | return self._logout(environ, start_response) | ||
193 | 145 | path = environ['PATH_INFO'] | 159 | path = environ['PATH_INFO'] |
194 | 146 | trailingSlashCount = len(path) - len(path.rstrip('/')) | 160 | trailingSlashCount = len(path) - len(path.rstrip('/')) |
195 | 147 | user = environ[self.session_var].get('user', LAUNCHPAD_ANONYMOUS) | 161 | user = environ[self.session_var].get('user', LAUNCHPAD_ANONYMOUS) |
196 | 148 | 162 | ||
197 | === modified file 'lib/launchpad_loggerhead/session.py' | |||
198 | --- lib/launchpad_loggerhead/session.py 2010-04-27 01:35:56 +0000 | |||
199 | +++ lib/launchpad_loggerhead/session.py 2010-05-11 22:19:24 +0000 | |||
200 | @@ -1,4 +1,4 @@ | |||
202 | 1 | # Copyright 2009 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2010 Canonical Ltd. This software is licensed under the |
203 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
204 | 3 | 3 | ||
205 | 4 | """Simple paste-y session manager tuned for the needs of launchpad-loggerhead. | 4 | """Simple paste-y session manager tuned for the needs of launchpad-loggerhead. |
206 | @@ -64,10 +64,17 @@ | |||
207 | 64 | session = pickle.loads(environ[self.session_var]) | 64 | session = pickle.loads(environ[self.session_var]) |
208 | 65 | else: | 65 | else: |
209 | 66 | session = {} | 66 | session = {} |
210 | 67 | existed = bool(session) | ||
211 | 67 | environ[self.session_var] = session | 68 | environ[self.session_var] = session |
212 | 68 | def response_hook(status, response_headers, exc_info=None): | 69 | def response_hook(status, response_headers, exc_info=None): |
213 | 69 | session = environ.pop(self.session_var) | 70 | session = environ.pop(self.session_var) |
215 | 70 | if session: | 71 | # paste.auth.cookie does not delete cookies (see |
216 | 72 | # http://trac.pythonpaste.org/pythonpaste/ticket/139). A | ||
217 | 73 | # reasonable workaround is to make the value empty. Therefore, | ||
218 | 74 | # we explicitly set the value in the session (to be encrypted) | ||
219 | 75 | # if the value is non-empty *or* if it was non-empty at the start | ||
220 | 76 | # of the request. | ||
221 | 77 | if existed or session: | ||
222 | 71 | environ[self.session_var] = pickle.dumps(session) | 78 | environ[self.session_var] = pickle.dumps(session) |
223 | 72 | return start_response(status, response_headers, exc_info) | 79 | return start_response(status, response_headers, exc_info) |
224 | 73 | return self.application(environ, response_hook) | 80 | return self.application(environ, response_hook) |
225 | 74 | 81 | ||
226 | === added file 'lib/launchpad_loggerhead/tests.py' | |||
227 | --- lib/launchpad_loggerhead/tests.py 1970-01-01 00:00:00 +0000 | |||
228 | +++ lib/launchpad_loggerhead/tests.py 2010-05-11 22:19:24 +0000 | |||
229 | @@ -0,0 +1,146 @@ | |||
230 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
231 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
232 | 3 | |||
233 | 4 | import unittest | ||
234 | 5 | import urllib | ||
235 | 6 | |||
236 | 7 | import lazr.uri | ||
237 | 8 | import wsgi_intercept | ||
238 | 9 | from wsgi_intercept.urllib2_intercept import install_opener, uninstall_opener | ||
239 | 10 | import wsgi_intercept.zope_testbrowser | ||
240 | 11 | from paste.httpexceptions import HTTPExceptionHandler | ||
241 | 12 | |||
242 | 13 | from canonical.config import config | ||
243 | 14 | from canonical.launchpad.webapp.vhosts import allvhosts | ||
244 | 15 | from canonical.testing import DatabaseFunctionalLayer | ||
245 | 16 | from launchpad_loggerhead.app import RootApp | ||
246 | 17 | from launchpad_loggerhead.session import SessionHandler | ||
247 | 18 | from lp.testing import TestCase | ||
248 | 19 | |||
249 | 20 | SESSION_VAR = 'lh.session' | ||
250 | 21 | |||
251 | 22 | # See sourcecode/launchpad-loggerhead/start-loggerhead.py for the production | ||
252 | 23 | # mechanism for getting the secret. | ||
253 | 24 | SECRET = 'secret' | ||
254 | 25 | |||
255 | 26 | |||
256 | 27 | def session_scribbler(app, test): | ||
257 | 28 | """Squirrel away the session variable.""" | ||
258 | 29 | def scribble(environ, start_response): | ||
259 | 30 | test.session = environ[SESSION_VAR] # Yay for mutables. | ||
260 | 31 | return app(environ, start_response) | ||
261 | 32 | return scribble | ||
262 | 33 | |||
263 | 34 | |||
264 | 35 | def dummy_destination(environ, start_response): | ||
265 | 36 | """Return a fake response.""" | ||
266 | 37 | start_response('200 OK', [('Content-type','text/plain')]) | ||
267 | 38 | return ['This is a dummy destination.\n'] | ||
268 | 39 | |||
269 | 40 | |||
270 | 41 | class SimpleLogInRootApp(RootApp): | ||
271 | 42 | """A mock root app that doesn't require open id.""" | ||
272 | 43 | def _complete_login(self, environ, start_response): | ||
273 | 44 | environ[SESSION_VAR]['user'] = 'bob' | ||
274 | 45 | start_response('200 OK', [('Content-type','text/plain')]) | ||
275 | 46 | return ['\n'] | ||
276 | 47 | |||
277 | 48 | |||
278 | 49 | class TestLogout(TestCase): | ||
279 | 50 | layer = DatabaseFunctionalLayer | ||
280 | 51 | |||
281 | 52 | def intercept(self, uri, app): | ||
282 | 53 | """Install wsgi interceptors for the uri, app tuple.""" | ||
283 | 54 | if isinstance(uri, basestring): | ||
284 | 55 | uri = lazr.uri.URI(uri) | ||
285 | 56 | port = uri.port | ||
286 | 57 | if port is None: | ||
287 | 58 | if uri.scheme == 'http': | ||
288 | 59 | port = 80 | ||
289 | 60 | elif uri.scheme == 'https': | ||
290 | 61 | port = 443 | ||
291 | 62 | else: | ||
292 | 63 | raise NotImplementedError(uri.scheme) | ||
293 | 64 | else: | ||
294 | 65 | port = int(port) | ||
295 | 66 | wsgi_intercept.add_wsgi_intercept(uri.host, port, lambda: app) | ||
296 | 67 | self.intercepted.append((uri.host, port)) | ||
297 | 68 | |||
298 | 69 | def setUp(self): | ||
299 | 70 | TestCase.setUp(self) | ||
300 | 71 | self.intercepted = [] | ||
301 | 72 | self.session = None | ||
302 | 73 | self.root = app = SimpleLogInRootApp(SESSION_VAR) | ||
303 | 74 | app = session_scribbler(app, self) | ||
304 | 75 | app = HTTPExceptionHandler(app) | ||
305 | 76 | app = SessionHandler(app, SESSION_VAR, SECRET) | ||
306 | 77 | self.cookie_name = app.cookie_handler.cookie_name | ||
307 | 78 | self.intercept(config.codehosting.codebrowse_root, app) | ||
308 | 79 | self.intercept(config.codehosting.secure_codebrowse_root, app) | ||
309 | 80 | self.intercept(allvhosts.configs['mainsite'].rooturl, | ||
310 | 81 | dummy_destination) | ||
311 | 82 | install_opener() | ||
312 | 83 | self.browser = wsgi_intercept.zope_testbrowser.WSGI_Browser() | ||
313 | 84 | # We want to pretend we are not a robot, or else mechanize will honor | ||
314 | 85 | # robots.txt. | ||
315 | 86 | self.browser.mech_browser.set_handle_robots(False) | ||
316 | 87 | self.browser.open( | ||
317 | 88 | config.codehosting.secure_codebrowse_root + '+login') | ||
318 | 89 | |||
319 | 90 | def tearDown(self): | ||
320 | 91 | uninstall_opener() | ||
321 | 92 | for host, port in self.intercepted: | ||
322 | 93 | wsgi_intercept.remove_wsgi_intercept(host, port) | ||
323 | 94 | TestCase.tearDown(self) | ||
324 | 95 | |||
325 | 96 | def testLoggerheadLogout(self): | ||
326 | 97 | # We start logged in as 'bob'. | ||
327 | 98 | self.assertEqual(self.session['user'], 'bob') | ||
328 | 99 | self.browser.open( | ||
329 | 100 | config.codehosting.secure_codebrowse_root + 'favicon.ico') | ||
330 | 101 | self.assertEqual(self.session['user'], 'bob') | ||
331 | 102 | self.failUnless(self.browser.cookies.get(self.cookie_name)) | ||
332 | 103 | |||
333 | 104 | # When we visit +logout, our session is gone. | ||
334 | 105 | self.browser.open( | ||
335 | 106 | config.codehosting.secure_codebrowse_root + '+logout') | ||
336 | 107 | self.assertEqual(self.session, {}) | ||
337 | 108 | |||
338 | 109 | # By default, we have been redirected to the Launchpad root. | ||
339 | 110 | self.assertEqual( | ||
340 | 111 | self.browser.url, allvhosts.configs['mainsite'].rooturl) | ||
341 | 112 | |||
342 | 113 | # The session cookie still exists, because of how | ||
343 | 114 | # paste.auth.cookie works (see | ||
344 | 115 | # http://trac.pythonpaste.org/pythonpaste/ticket/139 ) but the user | ||
345 | 116 | # does in fact have an empty session now. | ||
346 | 117 | self.browser.open( | ||
347 | 118 | config.codehosting.secure_codebrowse_root + 'favicon.ico') | ||
348 | 119 | self.assertEqual(self.session, {}) | ||
349 | 120 | |||
350 | 121 | def testLoggerheadLogoutRedirect(self): | ||
351 | 122 | # When we visit +logout with a 'next_to' value in the query string, | ||
352 | 123 | # the logout page will redirect to the given URI. As of this | ||
353 | 124 | # writing, this is used by Launchpad to redirect to our OpenId | ||
354 | 125 | # provider (see canonical.launchpad.tests.test_login. | ||
355 | 126 | # TestLoginAndLogout.test_CookieLogoutPage). | ||
356 | 127 | |||
357 | 128 | # Here, we will have a more useless example of the basic machinery. | ||
358 | 129 | dummy_root = 'http://dummy.dev/' | ||
359 | 130 | self.intercept(dummy_root, dummy_destination) | ||
360 | 131 | self.browser.open( | ||
361 | 132 | config.codehosting.secure_codebrowse_root + | ||
362 | 133 | '+logout?' + | ||
363 | 134 | urllib.urlencode(dict(next_to=dummy_root + '+logout'))) | ||
364 | 135 | |||
365 | 136 | # We are logged out, as before. | ||
366 | 137 | self.assertEqual(self.session, {}) | ||
367 | 138 | |||
368 | 139 | # Now, though, we are redirected to the ``next_to`` destination. | ||
369 | 140 | self.assertEqual(self.browser.url, dummy_root + '+logout') | ||
370 | 141 | self.assertEqual(self.browser.contents, | ||
371 | 142 | 'This is a dummy destination.\n') | ||
372 | 143 | |||
373 | 144 | |||
374 | 145 | def test_suite(): | ||
375 | 146 | return unittest.TestLoader().loadTestsFromName(__name__) | ||
376 | 0 | 147 | ||
377 | === modified file 'lib/lp/testopenid/browser/server.py' | |||
378 | --- lib/lp/testopenid/browser/server.py 2010-03-31 19:38:32 +0000 | |||
379 | +++ lib/lp/testopenid/browser/server.py 2010-05-11 22:19:24 +0000 | |||
380 | @@ -24,6 +24,7 @@ | |||
381 | 24 | from openid import oidutil | 24 | from openid import oidutil |
382 | 25 | from openid.server.server import CheckIDRequest, Server | 25 | from openid.server.server import CheckIDRequest, Server |
383 | 26 | from openid.store.memstore import MemoryStore | 26 | from openid.store.memstore import MemoryStore |
384 | 27 | from openid.extensions.sreg import SRegRequest, SRegResponse | ||
385 | 27 | 28 | ||
386 | 28 | from canonical.cachedproperty import cachedproperty | 29 | from canonical.cachedproperty import cachedproperty |
387 | 29 | from canonical.launchpad import _ | 30 | from canonical.launchpad import _ |
388 | @@ -41,6 +42,7 @@ | |||
389 | 41 | from lp.testopenid.interfaces.server import ( | 42 | from lp.testopenid.interfaces.server import ( |
390 | 42 | get_server_url, ITestOpenIDApplication, ITestOpenIDLoginForm, | 43 | get_server_url, ITestOpenIDApplication, ITestOpenIDLoginForm, |
391 | 43 | ITestOpenIDPersistentIdentity) | 44 | ITestOpenIDPersistentIdentity) |
392 | 45 | from lp.registry.interfaces.person import IPerson | ||
393 | 44 | 46 | ||
394 | 45 | 47 | ||
395 | 46 | OPENID_REQUEST_SESSION_KEY = 'testopenid.request' | 48 | OPENID_REQUEST_SESSION_KEY = 'testopenid.request' |
396 | @@ -196,6 +198,12 @@ | |||
397 | 196 | else: | 198 | else: |
398 | 197 | response = self.openid_request.answer(True) | 199 | response = self.openid_request.answer(True) |
399 | 198 | 200 | ||
400 | 201 | sreg_fields = dict(nickname=IPerson(self.account).name) | ||
401 | 202 | sreg_request = SRegRequest.fromOpenIDRequest(self.openid_request) | ||
402 | 203 | sreg_response = SRegResponse.extractResponse( | ||
403 | 204 | sreg_request, sreg_fields) | ||
404 | 205 | response.addExtension(sreg_response) | ||
405 | 206 | |||
406 | 199 | return response | 207 | return response |
407 | 200 | 208 | ||
408 | 201 | def createFailedResponse(self): | 209 | def createFailedResponse(self): |