10847.2.1
by Gary Poster
log out from bzr and openid after logging out from Launchpad |
1 |
# Copyright 2010 Canonical Ltd. This software is licensed under the
|
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
|
3 |
||
12329.1.3
by John Arbash Meinel
Log the failures as 'INFO' and test that they get logged. |
4 |
import cStringIO |
12329.1.1
by John Arbash Meinel
Fix bug #701329, don't generate an OOPS for socket-level errors. |
5 |
import errno |
12329.1.3
by John Arbash Meinel
Log the failures as 'INFO' and test that they get logged. |
6 |
import logging |
14612.2.5
by William Grant
Format the non-contrib bits of lib. |
7 |
import re |
8 |
import socket |
|
10847.2.1
by Gary Poster
log out from bzr and openid after logging out from Launchpad |
9 |
import urllib |
10 |
||
11 |
import lazr.uri |
|
14612.2.5
by William Grant
Format the non-contrib bits of lib. |
12 |
from paste import httpserver |
13 |
from paste.httpexceptions import HTTPExceptionHandler |
|
10847.2.1
by Gary Poster
log out from bzr and openid after logging out from Launchpad |
14 |
import wsgi_intercept |
14612.2.5
by William Grant
Format the non-contrib bits of lib. |
15 |
from wsgi_intercept.urllib2_intercept import ( |
16 |
install_opener, |
|
17 |
uninstall_opener, |
|
18 |
)
|
|
10847.2.1
by Gary Poster
log out from bzr and openid after logging out from Launchpad |
19 |
import wsgi_intercept.zope_testbrowser |
12329.1.1
by John Arbash Meinel
Fix bug #701329, don't generate an OOPS for socket-level errors. |
20 |
import zope.event |
10847.2.1
by Gary Poster
log out from bzr and openid after logging out from Launchpad |
21 |
|
14612.2.5
by William Grant
Format the non-contrib bits of lib. |
22 |
from launchpad_loggerhead.app import RootApp |
23 |
from launchpad_loggerhead.session import SessionHandler |
|
14605.1.1
by Curtis Hovey
Moved canonical.config to lp.services. |
24 |
from lp.services.config import config |
14600.2.2
by Curtis Hovey
Moved webapp to lp.services. |
25 |
from lp.services.webapp.vhosts import allvhosts |
14612.2.5
by William Grant
Format the non-contrib bits of lib. |
26 |
from lp.testing import TestCase |
14604.1.2
by Curtis Hovey
Fixed imports. |
27 |
from lp.testing.layers import DatabaseFunctionalLayer |
14612.2.5
by William Grant
Format the non-contrib bits of lib. |
28 |
|
10847.2.1
by Gary Poster
log out from bzr and openid after logging out from Launchpad |
29 |
|
30 |
SESSION_VAR = 'lh.session' |
|
31 |
||
32 |
# See sourcecode/launchpad-loggerhead/start-loggerhead.py for the production
|
|
33 |
# mechanism for getting the secret.
|
|
34 |
SECRET = 'secret' |
|
35 |
||
36 |
||
37 |
def session_scribbler(app, test): |
|
38 |
"""Squirrel away the session variable."""
|
|
39 |
def scribble(environ, start_response): |
|
40 |
test.session = environ[SESSION_VAR] # Yay for mutables. |
|
41 |
return app(environ, start_response) |
|
42 |
return scribble |
|
43 |
||
44 |
||
45 |
def dummy_destination(environ, start_response): |
|
46 |
"""Return a fake response."""
|
|
47 |
start_response('200 OK', [('Content-type','text/plain')]) |
|
48 |
return ['This is a dummy destination.\n'] |
|
49 |
||
50 |
||
51 |
class SimpleLogInRootApp(RootApp): |
|
52 |
"""A mock root app that doesn't require open id."""
|
|
53 |
def _complete_login(self, environ, start_response): |
|
54 |
environ[SESSION_VAR]['user'] = 'bob' |
|
55 |
start_response('200 OK', [('Content-type','text/plain')]) |
|
56 |
return ['\n'] |
|
57 |
||
58 |
||
59 |
class TestLogout(TestCase): |
|
60 |
layer = DatabaseFunctionalLayer |
|
61 |
||
62 |
def intercept(self, uri, app): |
|
63 |
"""Install wsgi interceptors for the uri, app tuple."""
|
|
64 |
if isinstance(uri, basestring): |
|
65 |
uri = lazr.uri.URI(uri) |
|
66 |
port = uri.port |
|
67 |
if port is None: |
|
68 |
if uri.scheme == 'http': |
|
69 |
port = 80 |
|
70 |
elif uri.scheme == 'https': |
|
71 |
port = 443 |
|
72 |
else: |
|
73 |
raise NotImplementedError(uri.scheme) |
|
74 |
else: |
|
75 |
port = int(port) |
|
76 |
wsgi_intercept.add_wsgi_intercept(uri.host, port, lambda: app) |
|
77 |
self.intercepted.append((uri.host, port)) |
|
78 |
||
79 |
def setUp(self): |
|
80 |
TestCase.setUp(self) |
|
81 |
self.intercepted = [] |
|
82 |
self.session = None |
|
83 |
self.root = app = SimpleLogInRootApp(SESSION_VAR) |
|
84 |
app = session_scribbler(app, self) |
|
85 |
app = HTTPExceptionHandler(app) |
|
86 |
app = SessionHandler(app, SESSION_VAR, SECRET) |
|
87 |
self.cookie_name = app.cookie_handler.cookie_name |
|
88 |
self.intercept(config.codehosting.codebrowse_root, app) |
|
89 |
self.intercept(config.codehosting.secure_codebrowse_root, app) |
|
90 |
self.intercept(allvhosts.configs['mainsite'].rooturl, |
|
91 |
dummy_destination) |
|
92 |
install_opener() |
|
93 |
self.browser = wsgi_intercept.zope_testbrowser.WSGI_Browser() |
|
94 |
# We want to pretend we are not a robot, or else mechanize will honor
|
|
95 |
# robots.txt.
|
|
96 |
self.browser.mech_browser.set_handle_robots(False) |
|
97 |
self.browser.open( |
|
98 |
config.codehosting.secure_codebrowse_root + '+login') |
|
99 |
||
100 |
def tearDown(self): |
|
101 |
uninstall_opener() |
|
102 |
for host, port in self.intercepted: |
|
103 |
wsgi_intercept.remove_wsgi_intercept(host, port) |
|
104 |
TestCase.tearDown(self) |
|
105 |
||
106 |
def testLoggerheadLogout(self): |
|
107 |
# We start logged in as 'bob'.
|
|
108 |
self.assertEqual(self.session['user'], 'bob') |
|
109 |
self.browser.open( |
|
110 |
config.codehosting.secure_codebrowse_root + 'favicon.ico') |
|
111 |
self.assertEqual(self.session['user'], 'bob') |
|
112 |
self.failUnless(self.browser.cookies.get(self.cookie_name)) |
|
113 |
||
114 |
# When we visit +logout, our session is gone.
|
|
115 |
self.browser.open( |
|
116 |
config.codehosting.secure_codebrowse_root + '+logout') |
|
117 |
self.assertEqual(self.session, {}) |
|
118 |
||
119 |
# By default, we have been redirected to the Launchpad root.
|
|
120 |
self.assertEqual( |
|
121 |
self.browser.url, allvhosts.configs['mainsite'].rooturl) |
|
122 |
||
123 |
# The session cookie still exists, because of how
|
|
124 |
# paste.auth.cookie works (see
|
|
125 |
# http://trac.pythonpaste.org/pythonpaste/ticket/139 ) but the user
|
|
126 |
# does in fact have an empty session now.
|
|
127 |
self.browser.open( |
|
128 |
config.codehosting.secure_codebrowse_root + 'favicon.ico') |
|
129 |
self.assertEqual(self.session, {}) |
|
130 |
||
131 |
def testLoggerheadLogoutRedirect(self): |
|
132 |
# When we visit +logout with a 'next_to' value in the query string,
|
|
133 |
# the logout page will redirect to the given URI. As of this
|
|
134 |
# writing, this is used by Launchpad to redirect to our OpenId
|
|
14600.1.6
by Curtis Hovey
Removed empty directory. |
135 |
# provider (see lp.testing.tests.test_login.
|
10847.2.1
by Gary Poster
log out from bzr and openid after logging out from Launchpad |
136 |
# TestLoginAndLogout.test_CookieLogoutPage).
|
137 |
||
138 |
# Here, we will have a more useless example of the basic machinery.
|
|
139 |
dummy_root = 'http://dummy.dev/' |
|
140 |
self.intercept(dummy_root, dummy_destination) |
|
141 |
self.browser.open( |
|
142 |
config.codehosting.secure_codebrowse_root + |
|
143 |
'+logout?' + |
|
144 |
urllib.urlencode(dict(next_to=dummy_root + '+logout'))) |
|
145 |
||
146 |
# We are logged out, as before.
|
|
147 |
self.assertEqual(self.session, {}) |
|
148 |
||
149 |
# Now, though, we are redirected to the ``next_to`` destination.
|
|
150 |
self.assertEqual(self.browser.url, dummy_root + '+logout') |
|
151 |
self.assertEqual(self.browser.contents, |
|
152 |
'This is a dummy destination.\n') |