Merge lp:~salgado/launchpad/separate-cookie-for-testopenid into lp:launchpad
- separate-cookie-for-testopenid
- Merge into devel
Proposed by
Guilherme Salgado
Status: | Rejected |
---|---|
Rejected by: | Guilherme Salgado |
Proposed branch: | lp:~salgado/launchpad/separate-cookie-for-testopenid |
Merge into: | lp:launchpad |
Prerequisite: | lp:~salgado/launchpad/lp-as-openid-rp |
Diff against target: |
493 lines (+292/-28) 9 files modified
lib/canonical/config/schema-lazr.conf (+5/-0) lib/canonical/launchpad/webapp/authentication.py (+2/-2) lib/canonical/launchpad/webapp/dbpolicy.py (+2/-2) lib/canonical/launchpad/webapp/login.py (+3/-2) lib/canonical/launchpad/webapp/pgsession.py (+3/-3) lib/canonical/launchpad/webapp/session.py (+109/-17) lib/canonical/launchpad/webapp/tests/test_cookie_client_id_manager.txt (+158/-0) lib/canonical/launchpad/webapp/tests/test_dbpolicy.py (+1/-1) lib/canonical/launchpad/webapp/tests/test_session.py (+9/-1) |
To merge this branch: | bzr merge lp:~salgado/launchpad/separate-cookie-for-testopenid |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gary Poster (community) | Approve | ||
Review via email: mp+18018@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Guilherme Salgado (salgado) wrote : | # |
Revision history for this message
Guilherme Salgado (salgado) wrote : | # |
This might not be needed, so not yet ready for review.
Revision history for this message
Gary Poster (gary) wrote : | # |
Good.
As we discussed on IRC, we both agree that it will be nice to revert this once it is no longer necessary.
Because of that, and for general readability, I'd like to have the cut-n-paste blocks from Zope code more clearly marked. You have a nice comment at the end of one of those blocks ("# From here on it's our custom code...") but I'd like to see a start marker and end marker for these blocks, and a comment that this change is slated to be reverted.
Other than that, I'm happy with it.
Thanks,
Gary
review:
Approve
Revision history for this message
Guilherme Salgado (salgado) wrote : | # |
I've made it clear where the duplication is and filed bug 520582 (linking to this MP), so that I don't forget to revert the changes later on.
Unmerged revisions
- 10218. By Guilherme Salgado
-
merge from loomified branch
- 10217. By Guilherme Salgado
-
merge from mainline
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/canonical/config/schema-lazr.conf' |
2 | --- lib/canonical/config/schema-lazr.conf 2010-02-11 16:33:20 +0000 |
3 | +++ lib/canonical/config/schema-lazr.conf 2010-02-11 16:33:22 +0000 |
4 | @@ -1114,6 +1114,11 @@ |
5 | # datatype: string |
6 | cookie: launchpad |
7 | |
8 | +# The id of the cookie used to store the session token for the testopenid |
9 | +# service. |
10 | +# datatype: string |
11 | +testopenid_cookie: testopenid |
12 | + |
13 | |
14 | [librarianlogparser] |
15 | logs_root = /srv/launchpadlibrarian.net-logs |
16 | |
17 | === modified file 'lib/canonical/launchpad/webapp/authentication.py' |
18 | --- lib/canonical/launchpad/webapp/authentication.py 2009-10-20 22:13:21 +0000 |
19 | +++ lib/canonical/launchpad/webapp/authentication.py 2010-02-11 16:33:22 +0000 |
20 | @@ -22,6 +22,7 @@ |
21 | from zope.interface import implements |
22 | from zope.component import getUtility |
23 | from zope.event import notify |
24 | +from zope.session.interfaces import IClientIdManager |
25 | |
26 | from zope.security.proxy import removeSecurityProxy |
27 | |
28 | @@ -29,7 +30,6 @@ |
29 | from zope.app.security.interfaces import ILoginPassword |
30 | from zope.app.security.principalregistry import UnauthenticatedPrincipal |
31 | |
32 | -from canonical.config import config |
33 | from canonical.launchpad.interfaces.account import IAccountSet |
34 | from canonical.launchpad.interfaces.launchpad import IPasswordEncryptor |
35 | from canonical.launchpad.interfaces.oauth import OAUTH_CHALLENGE |
36 | @@ -119,7 +119,7 @@ |
37 | # Hack to make us not even think of using a session if there |
38 | # isn't already a cookie in the request, or one waiting to be |
39 | # set in the response. |
40 | - cookie_name = config.launchpad_session.cookie |
41 | + cookie_name = getUtility(IClientIdManager).getNamespace(request) |
42 | if (request.cookies.get(cookie_name) is not None or |
43 | request.response.getCookie(cookie_name) is not None): |
44 | return self._authenticateUsingCookieAuth(request) |
45 | |
46 | === modified file 'lib/canonical/launchpad/webapp/dbpolicy.py' |
47 | --- lib/canonical/launchpad/webapp/dbpolicy.py 2010-01-20 22:09:26 +0000 |
48 | +++ lib/canonical/launchpad/webapp/dbpolicy.py 2010-02-11 16:33:22 +0000 |
49 | @@ -184,7 +184,7 @@ |
50 | |
51 | def _hasSession(self): |
52 | "Is there is already a session cookie hanging around?" |
53 | - cookie_name = getUtility(IClientIdManager).namespace |
54 | + cookie_name = getUtility(IClientIdManager).getNamespace(self.request) |
55 | return ( |
56 | cookie_name in self.request.cookies or |
57 | self.request.response.getCookie(cookie_name) is not None) |
58 | @@ -325,7 +325,7 @@ |
59 | # standard Launchpad database policy for load balancing to |
60 | # the slave databases. The javascript web service libraries |
61 | # send the session cookie for authenticated users. |
62 | - cookie_name = getUtility(IClientIdManager).namespace |
63 | + cookie_name = getUtility(IClientIdManager).getNamespace(request) |
64 | if cookie_name in request.cookies: |
65 | return LaunchpadDatabasePolicy(request) |
66 | # Otherwise, use the master only web service database policy. |
67 | |
68 | === modified file 'lib/canonical/launchpad/webapp/login.py' |
69 | --- lib/canonical/launchpad/webapp/login.py 2010-01-14 13:25:34 +0000 |
70 | +++ lib/canonical/launchpad/webapp/login.py 2010-02-11 16:33:22 +0000 |
71 | @@ -471,7 +471,7 @@ |
72 | delta=timedelta(minutes=10)): |
73 | if client_id_manager is None: |
74 | client_id_manager = getUtility(IClientIdManager) |
75 | - session_cookiename = client_id_manager.namespace |
76 | + session_cookiename = client_id_manager.getNamespace(request) |
77 | value = request.response.getCookie(session_cookiename)['value'] |
78 | expiration = (datetime.utcnow() + delta).strftime( |
79 | '%a, %d %b %Y %H:%M:%S GMT') |
80 | @@ -492,7 +492,8 @@ |
81 | if not IUnauthenticatedPrincipal.providedBy(request.principal): |
82 | return |
83 | client_id_manager = getUtility(IClientIdManager) |
84 | - if request.response.getCookie(client_id_manager.namespace) is None: |
85 | + cookie_name = client_id_manager.getNamespace(request) |
86 | + if request.response.getCookie(cookie_name) is None: |
87 | client_id_manager.setRequestId( |
88 | request, client_id_manager.getClientId(request)) |
89 | expireSessionCookie(request, client_id_manager, duration) |
90 | |
91 | === modified file 'lib/canonical/launchpad/webapp/pgsession.py' |
92 | --- lib/canonical/launchpad/webapp/pgsession.py 2009-11-30 08:57:54 +0000 |
93 | +++ lib/canonical/launchpad/webapp/pgsession.py 2010-02-11 16:33:22 +0000 |
94 | @@ -115,9 +115,9 @@ |
95 | if IUnauthenticatedPrincipal.providedBy(request.principal): |
96 | # it would be nice if this could be a monitored, logged |
97 | # message instead of an instant-OOPS. |
98 | - assert (client_id_manager.namespace in request.cookies or |
99 | - request.response.getCookie( |
100 | - client_id_manager.namespace) is not None), ( |
101 | + namespace = client_id_manager.getNamespace(request) |
102 | + assert (namespace in request.cookies or |
103 | + request.response.getCookie(namespace) is not None), ( |
104 | 'Session data should generally only be stored for ' |
105 | 'authenticated users, and for users who have just logged ' |
106 | 'out. If an unauthenticated user has just logged out, ' |
107 | |
108 | === modified file 'lib/canonical/launchpad/webapp/session.py' |
109 | --- lib/canonical/launchpad/webapp/session.py 2009-06-25 05:30:52 +0000 |
110 | +++ lib/canonical/launchpad/webapp/session.py 2010-02-11 16:33:22 +0000 |
111 | @@ -5,13 +5,21 @@ |
112 | |
113 | __metaclass__ = type |
114 | |
115 | +import hmac |
116 | +import logging |
117 | +import time |
118 | +from email.utils import formatdate |
119 | +from hashlib import sha1 |
120 | + |
121 | from cookielib import domain_match |
122 | from zope.component import getUtility |
123 | -from zope.session.http import CookieClientIdManager |
124 | +from zope.publisher.interfaces.http import IHTTPApplicationRequest |
125 | +from zope.session.http import CookieClientIdManager, digestEncode |
126 | |
127 | from storm.zope.interfaces import IZStorm |
128 | |
129 | from canonical.config import config |
130 | +from canonical.launchpad.layers import TestOpenIDLayer |
131 | from canonical.launchpad.webapp.url import urlparse |
132 | |
133 | |
134 | @@ -20,6 +28,7 @@ |
135 | HOURS = 60 * MINUTES |
136 | DAYS = 24 * HOURS |
137 | YEARS = 365 * DAYS |
138 | +logger = logging.getLogger() |
139 | |
140 | |
141 | def get_cookie_domain(request_domain): |
142 | @@ -45,13 +54,33 @@ |
143 | class LaunchpadCookieClientIdManager(CookieClientIdManager): |
144 | |
145 | def __init__(self): |
146 | - CookieClientIdManager.__init__(self) |
147 | - self.namespace = config.launchpad_session.cookie |
148 | + # Don't upcall to the parent here as it will only set self.namespace |
149 | + # and self.secret, which we don't want as we need to use |
150 | + # getNamespace(request) and we have a @property for secret. |
151 | + |
152 | # Set the cookie life time to something big. |
153 | # It should be larger than our session expiry time. |
154 | self.cookieLifetime = 1 * YEARS |
155 | self._secret = None |
156 | |
157 | + @property |
158 | + def namespace(self): |
159 | + raise AttributeError( |
160 | + "This class doesn't provide a namespace attribute because it " |
161 | + "needs a request to figure out the correct namespace. Use " |
162 | + "getNamespace(request) instead.") |
163 | + |
164 | + def getNamespace(self, request): |
165 | + """Return the correct namespace according to the given request. |
166 | + |
167 | + This is needed so that we can use a separate cookie for the testopenid |
168 | + vhost. |
169 | + """ |
170 | + if TestOpenIDLayer.providedBy(request): |
171 | + return config.launchpad_session.testopenid_cookie |
172 | + else: |
173 | + return config.launchpad_session.cookie |
174 | + |
175 | def getClientId(self, request): |
176 | sid = self.getRequestId(request) |
177 | if sid is None: |
178 | @@ -69,7 +98,8 @@ |
179 | request.annotations[ANNOTATION_KEY] = sid |
180 | return sid |
181 | |
182 | - def _get_secret(self): |
183 | + @property |
184 | + def secret(self): |
185 | # Because our CookieClientIdManager is not persistent, we need to |
186 | # pull the secret from some other data store - failing to do this |
187 | # would mean a new secret is generated every time the server is |
188 | @@ -82,16 +112,44 @@ |
189 | self._secret = result.get_one()[0] |
190 | return self._secret |
191 | |
192 | - def _set_secret(self, value): |
193 | - # Silently ignored so CookieClientIdManager's __init__ method |
194 | - # doesn't die |
195 | - pass |
196 | + # Re-implement the parent's method so that we can use |
197 | + # self.getNamespace(request) instead of self.namespace. |
198 | + def getRequestId(self, request): |
199 | + """Return the browser id encoded in request as a string |
200 | |
201 | - secret = property(_get_secret, _set_secret) |
202 | + Return None if an id is not set. |
203 | + """ |
204 | + cookie_name = self.getNamespace(request) |
205 | + response_cookie = request.response.getCookie(cookie_name) |
206 | + if response_cookie: |
207 | + sid = response_cookie['value'] |
208 | + else: |
209 | + request = IHTTPApplicationRequest(request) |
210 | + sid = request.getCookies().get(cookie_name, None) |
211 | + if self.thirdparty: |
212 | + return sid |
213 | + else: |
214 | + # If there is an id set on the response, use that but |
215 | + # don't trust it. We need to check the response in case |
216 | + # there has already been a new session created during the |
217 | + # course of this request. |
218 | + if sid is None or len(sid) != 54: |
219 | + return None |
220 | + s, mac = sid[:27], sid[27:] |
221 | + |
222 | + # call s.encode() to workaround a bug where the hmac |
223 | + # module only accepts str() types in Python 2.6 |
224 | + if (digestEncode(hmac.new( |
225 | + s.encode(), self.secret, digestmod=sha1 |
226 | + ).digest()) != mac): |
227 | + return None |
228 | + else: |
229 | + return sid |
230 | |
231 | def setRequestId(self, request, id): |
232 | - """As per CookieClientIdManager.setRequestID, except |
233 | - we force the domain key on the cookie to be set to allow our |
234 | + """Set cookie with id on request. |
235 | + |
236 | + Also force the domain key on the cookie to be set to allow our |
237 | session to be shared between virtual hosts where possible, and |
238 | we set the secure key to stop the session key being sent to |
239 | insecure URLs like the Librarian. |
240 | @@ -99,12 +157,46 @@ |
241 | We also log the referrer url on creation of a new |
242 | requestid so we can track where first time users arrive from. |
243 | """ |
244 | - # XXX: SteveAlexander, 2007-04-01. |
245 | - # This is on the codepath where anon users get a session cookie |
246 | - # set unnecessarily. |
247 | - CookieClientIdManager.setRequestId(self, request, id) |
248 | - |
249 | - cookie = request.response.getCookie(self.namespace) |
250 | + # TODO: Currently, the path is the ApplicationURL. This is reasonable, |
251 | + # and will be adequate for most purposes. |
252 | + # A better path to use would be that of the folder that contains |
253 | + # the site manager this service is registered within. However, |
254 | + # that would be expensive to look up on each request, and would |
255 | + # have to be altered to take virtual hosting into account. |
256 | + # Seeing as this utility instance has a unique namespace for its |
257 | + # cookie, using ApplicationURL shouldn't be a problem. |
258 | + |
259 | + if self.thirdparty: |
260 | + logger.warning( |
261 | + 'ClientIdManager is using thirdparty cookies, ignoring ' |
262 | + 'setRequestId call') |
263 | + return |
264 | + |
265 | + response = request.response |
266 | + options = {} |
267 | + if self.cookieLifetime is not None: |
268 | + if self.cookieLifetime: |
269 | + expires = formatdate(time.time() + self.cookieLifetime, |
270 | + localtime=False, usegmt=True) |
271 | + else: |
272 | + expires = 'Tue, 19 Jan 2038 00:00:00 GMT' |
273 | + |
274 | + options['expires'] = expires |
275 | + |
276 | + response.setCookie( |
277 | + self.getNamespace(request), id, |
278 | + path=request.getApplicationURL(path_only=True), |
279 | + **options) |
280 | + |
281 | + response.setHeader( |
282 | + 'Cache-Control', 'no-cache="Set-Cookie,Set-Cookie2"') |
283 | + response.setHeader('Pragma', 'no-cache') |
284 | + response.setHeader('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT') |
285 | + |
286 | + # From here on it's our custom code -- everything else in this method |
287 | + # is copied from zope.session.http, replacing just self.namespace with |
288 | + # self.getNamespace(request). |
289 | + cookie = request.response.getCookie(self.getNamespace(request)) |
290 | protocol, request_domain = urlparse(request.getURL())[:2] |
291 | |
292 | # Set secure flag on cookie. |
293 | |
294 | === added file 'lib/canonical/launchpad/webapp/tests/test_cookie_client_id_manager.txt' |
295 | --- lib/canonical/launchpad/webapp/tests/test_cookie_client_id_manager.txt 1970-01-01 00:00:00 +0000 |
296 | +++ lib/canonical/launchpad/webapp/tests/test_cookie_client_id_manager.txt 2010-02-11 16:33:22 +0000 |
297 | @@ -0,0 +1,158 @@ |
298 | += Cookie Client ID Manager = |
299 | + |
300 | +We use a custom ICookieClientIdManager which instead of a 'namespace' |
301 | +attribute provides a getNamespace(request) method. That's because we sometimes |
302 | +need to change the namespace depending on the request's layer (e.g. when the |
303 | +request is on the TestOpenID layer we want a separate cookie from the |
304 | +Launchpad one). |
305 | + |
306 | +Thanks to this requirement, our custom class has to re-implement a couple |
307 | +methods from CookieClientIdManager that expect the 'namespace' attribute to |
308 | +be provided. |
309 | + |
310 | + >>> from canonical.launchpad.webapp.session import ( |
311 | + ... LaunchpadCookieClientIdManager) |
312 | + >>> bim = LaunchpadCookieClientIdManager() |
313 | + >>> bim.namespace |
314 | + Traceback (most recent call last): |
315 | + ... |
316 | + AttributeError: This class doesn't provide a namespace attribute... |
317 | + |
318 | + >>> from StringIO import StringIO |
319 | + >>> from zope.publisher.http import HTTPRequest |
320 | + >>> environ = {'SERVER_URL': 'http://launchpad.dev'} |
321 | + >>> request = HTTPRequest(StringIO(''), environ, None) |
322 | + >>> bim.getNamespace(request) |
323 | + 'launchpad_tests' |
324 | + |
325 | +When the request is in the TestOpenIDLayer, a separate cookie is used. |
326 | + |
327 | + >>> from canonical.launchpad.layers import setFirstLayer, TestOpenIDLayer |
328 | + >>> testopenid_request = HTTPRequest(StringIO(''), environ, None) |
329 | + >>> setFirstLayer(testopenid_request, TestOpenIDLayer) |
330 | + >>> bim.getNamespace(testopenid_request) |
331 | + 'testopenid' |
332 | + |
333 | + |
334 | +== getRequestId(request) == |
335 | + |
336 | +Because no cookie has been set, we get no id: |
337 | + |
338 | + >>> bim.getRequestId(request) is None |
339 | + True |
340 | + |
341 | +We can set an id: |
342 | + |
343 | + >>> id1 = bim.generateUniqueId() |
344 | + >>> bim.setRequestId(request, id1) |
345 | + |
346 | +And get it back: |
347 | + |
348 | + >>> bim.getRequestId(request) == id1 |
349 | + True |
350 | + |
351 | +When we set the request id, we also set a response cookie. We |
352 | +can simulate getting this cookie back in a subsequent request: |
353 | + |
354 | + >>> request2 = HTTPRequest(StringIO(''), environ, None) |
355 | + >>> request2._cookies = dict( |
356 | + ... [(name, cookie['value']) |
357 | + ... for (name, cookie) in request.response._cookies.items() |
358 | + ... ]) |
359 | + |
360 | +And we get the same id back from the new request: |
361 | + |
362 | + >>> bim.getRequestId(request) == bim.getRequestId(request2) |
363 | + True |
364 | + |
365 | +Test a corner case where Python 2.6 hmac module does not allow |
366 | +unicode as input: |
367 | + |
368 | + >>> id_uni = unicode(bim.generateUniqueId()) |
369 | + >>> bim.setRequestId(request, id_uni) |
370 | + >>> bim.getRequestId(request) == id_uni |
371 | + True |
372 | + |
373 | +If another server is managing the ClientId cookies (Apache, Nginx) |
374 | +we're returning their value without checking: |
375 | + |
376 | + >>> bim.thirdparty = True |
377 | + >>> request3 = HTTPRequest(StringIO(''), environ, None) |
378 | + >>> request3._cookies = { |
379 | + ... bim.getNamespace(request3): 'AQAAf0Y4gjgAAAQ3AwMEAg=='} |
380 | + >>> bim.getRequestId(request3) |
381 | + 'AQAAf0Y4gjgAAAQ3AwMEAg==' |
382 | + |
383 | + |
384 | +== setRequestId(request, id) == |
385 | + |
386 | +Note that the id is checked for validity. Setting an |
387 | +invalid value is silently ignored: |
388 | + |
389 | + >>> request = HTTPRequest(StringIO(''), environ, None) |
390 | + >>> bim = LaunchpadCookieClientIdManager() |
391 | + >>> bim.getRequestId(request) |
392 | + >>> bim.setRequestId(request, 'invalid id') |
393 | + >>> bim.getRequestId(request) |
394 | + |
395 | +For now, the cookie path is the application URL: |
396 | + |
397 | + >>> cookie = request.response.getCookie(bim.getNamespace(request)) |
398 | + >>> cookie['path'] == request.getApplicationURL(path_only=True) |
399 | + True |
400 | + |
401 | +By default, session cookies have a lifetime of 1 year: |
402 | + |
403 | + >>> bim.cookieLifetime # 1 year in seconds. |
404 | + 31536000 |
405 | + >>> cookie.has_key('expires') |
406 | + True |
407 | + |
408 | +Expiry time of 0 means never (well - close enough) |
409 | + |
410 | + >>> bim.cookieLifetime = 0 |
411 | + >>> request = HTTPRequest(StringIO(''), environ, None) |
412 | + >>> sid = bim.getRequestId(request) |
413 | + >>> bim.setRequestId(request, sid) |
414 | + >>> cookie = request.response.getCookie(bim.getNamespace(request)) |
415 | + >>> cookie['expires'] |
416 | + 'Tue, 19 Jan 2038 00:00:00 GMT' |
417 | + |
418 | +If another server in front of Zope (Apache, Nginx) is managing the |
419 | +cookies we won't set any ClientId cookies: |
420 | + |
421 | + >>> request = HTTPRequest(StringIO(''), environ, None) |
422 | + >>> bim.thirdparty = True |
423 | + >>> bim.setRequestId(request, '1234') |
424 | + WARNING:root:ClientIdManager is using thirdparty cookies... |
425 | + >>> cookie = request.response.getCookie(bim.getNamespace(request)) |
426 | + >>> print cookie |
427 | + None |
428 | + |
429 | +For https requests, the secure cookie option is included. |
430 | + |
431 | + >>> bim.thirdparty = False |
432 | + >>> bim.cookieLifetime = None |
433 | + >>> request = HTTPRequest( |
434 | + ... StringIO(''), {'SERVER_URL': 'https://example.com'}, None) |
435 | + >>> bim.setRequestId(request, '1234') |
436 | + >>> print request.response.getCookie(bim.getNamespace(request)) |
437 | + {'path': '/', 'secure': True, 'value': '1234'} |
438 | + |
439 | +But for http requests, it is not. |
440 | + |
441 | + >>> request = HTTPRequest( |
442 | + ... StringIO(''), {'SERVER_URL': 'http://example.com'}, None) |
443 | + >>> bim.setRequestId(request, '1234') |
444 | + >>> print request.response.getCookie(bim.getNamespace(request)) |
445 | + {'path': '/', 'secure': False, 'value': '1234'} |
446 | + |
447 | +When the cookie is set, cache headers are added to the |
448 | +response to try to prevent the cookie header from being cached: |
449 | + |
450 | + >>> request.response.getHeader('Cache-Control') |
451 | + 'no-cache="Set-Cookie,Set-Cookie2"' |
452 | + >>> request.response.getHeader('Pragma') |
453 | + 'no-cache' |
454 | + >>> request.response.getHeader('Expires') |
455 | + 'Mon, 26 Jul 1997 05:00:00 GMT' |
456 | |
457 | === modified file 'lib/canonical/launchpad/webapp/tests/test_dbpolicy.py' |
458 | --- lib/canonical/launchpad/webapp/tests/test_dbpolicy.py 2010-01-13 20:06:09 +0000 |
459 | +++ lib/canonical/launchpad/webapp/tests/test_dbpolicy.py 2010-02-11 16:33:22 +0000 |
460 | @@ -191,7 +191,7 @@ |
461 | newInteraction(request) |
462 | try: |
463 | # First, generate a valid session cookie. |
464 | - cookie_name = getUtility(IClientIdManager).namespace |
465 | + cookie_name = getUtility(IClientIdManager).getNamespace(request) |
466 | ISession(request)['whatever']['whatever'] = 'whatever' |
467 | # Then stuff it into the request where we expect to |
468 | # find it. The database policy is only interested if |
469 | |
470 | === modified file 'lib/canonical/launchpad/webapp/tests/test_session.py' |
471 | --- lib/canonical/launchpad/webapp/tests/test_session.py 2009-06-25 05:30:52 +0000 |
472 | +++ lib/canonical/launchpad/webapp/tests/test_session.py 2010-02-11 16:33:22 +0000 |
473 | @@ -3,6 +3,9 @@ |
474 | |
475 | import unittest |
476 | |
477 | +from canonical.testing import DatabaseFunctionalLayer |
478 | +from canonical.launchpad.testing.systemdocs import ( |
479 | + LayeredDocFileSuite, setUp, tearDown) |
480 | from canonical.launchpad.webapp.session import get_cookie_domain |
481 | |
482 | |
483 | @@ -44,4 +47,9 @@ |
484 | |
485 | |
486 | def test_suite(): |
487 | - return unittest.TestLoader().loadTestsFromName(__name__) |
488 | + suite = unittest.TestSuite() |
489 | + suite.addTest(LayeredDocFileSuite( |
490 | + 'test_cookie_client_id_manager.txt', |
491 | + layer=DatabaseFunctionalLayer, setUp=setUp, tearDown=tearDown)) |
492 | + suite.addTests(unittest.TestLoader().loadTestsFromName(__name__)) |
493 | + return suite |
This branch changes our custom CookieClientIDM anager to allow us to use a separate cookie for requests in the TestOpenIDLayer. This is needed so that the cookie you get when logging into testopenid.lp.dev is not picked up in lp.dev