~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
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')