~launchpad-pqm/launchpad/devel

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