views:

243

answers:

2

I'm having an issue running unit tests for authorization in a Pylons app. It appears as though certain cookies set in the test case may not be correctly written or parsed. Cookies work fine when hitting the app with a browser.

Here is my test case inside a paste-generated TestController:

def test_good_login(self):
    r = self.app.post('/dologin', params={'login': self.user['username'], 'password': self.password})
    r = r.follow() # Should only be one redirect to root
    assert 'http://localhost/' == r.request.url
    assert 'Dashboard' in r

This is supposed to test that a login of an existing account forwards the user to the dashboard page. Instead, what happens is that the user is redirected back to the login. The first POST works, sets the user in the session and returns cookies. Although those cookies are sent in the follow request, they don't seem to be correctly parsed.

I start by setting a breakpoint at the beginning of the above method and see what the login response returns:

> nosetests --pdb --pdb-failure -s foo.tests.functional.test_account:TestMainController.test_good_login
Running setup_config() from foo.websetup
> /Users/istevens/dev/foo/foo/tests/functional/test_account.py(33)test_good_login()
-> r = self.app.post('/dologin', params={'login': self.user['username'], 'password': self.password})
(Pdb) n
> /Users/istevens/dev/foo/foo/tests/functional/test_account.py(34)test_good_login()
-> r = r.follow() # Should only be one redirect to root
(Pdb) p r.cookies_set
{'auth_tkt': '"4c898eb72f7ad38551eb11e1936303374bd871934bd871833d19ad8a79000000!"'}
(Pdb) p r.request.environ['REMOTE_USER']
'4bd871833d19ad8a79000000'
(Pdb) p r.headers['Location']
'http://localhost/?__logins=0'

A session appears to be created and a cookie sent back. The browser is redirected to the root, not the login, which also indicates a successful login. If I step past the follow(), I get:

> /Users/istevens/dev/foo/foo/tests/functional/test_account.py(35)test_good_login()
-> assert 'http://localhost/' == r.request.url
(Pdb) p r.request.headers
{'Host': 'localhost:80', 'Cookie': 'auth_tkt=""\\"4c898eb72f7ad38551eb11e1936303374bd871934bd871833d19ad8a79000000!\\"""; '}
(Pdb) p r.request.environ['REMOTE_USER']
*** KeyError: KeyError('REMOTE_USER',)
(Pdb) p r.request.environ['HTTP_COOKIE']
'auth_tkt=""\\"4c898eb72f7ad38551eb11e1936303374bd871934bd871833d19ad8a79000000!\\"""; '
(Pdb) p r.request.cookies
{'auth_tkt': ''}
(Pdb) p r
<302 Found text/html location: http://localhost/login?__logins=1&amp;came_from=http%3A%2F%2Flocalhost%2F body='302 Found...y.  '/149>

This indicates to me that the cookie was passed in on the request, although with dubious escaping. The environ appears to be without the session created on the prior request. The cookie has been copied to the environ from the headers, but the cookies in the request seems incorrectly set. Lastly, the user is redirected to the login page, indicating that the user isn't logged in.

Authorization in the app is done via repoze.who and repoze.who.plugins.ldap with repoze.who_friendlyform performing the challenge. I'm using the stock tests.TestController created by paste:

class TestController(TestCase):

    def __init__(self, *args, **kwargs):
        if pylons.test.pylonsapp:
            wsgiapp = pylons.test.pylonsapp
        else:
            wsgiapp = loadapp('config:%s' % config['__file__'])
        self.app = TestApp(wsgiapp)
        url._push_object(URLGenerator(config['routes.map'], environ))
        TestCase.__init__(self, *args, **kwargs)

That's a webtest.TestApp, by the way.

The encoding of the cookie is done in webtest.TestApp using Cookie:

>>> from Cookie import _quote
>>> _quote('"84533cf9f661f97239208fb844a09a6d4bd8552d4bd8550c3d19ad8339000000!"')
'"\\"84533cf9f661f97239208fb844a09a6d4bd8552d4bd8550c3d19ad8339000000!\\""'

I trust that that's correct.

My guess is that something on the response side is incorrectly parsing the cookie data into cookies in the server-side request. But what? Any ideas?

+4  A: 

This issue disappeared after downgrading WebTest from 1.2.1 to 1.2.

Ian Stevens
howto downgrade using pip:$ pip install webtest==1.2
Karl R
+1  A: 

The issue continually appeared for me regardless of the version of WebTest. However, after much mucking around I noticed that when the cookie was first set it was using 127.0.0.1 as the REMOTE_ADDR value but on the second request it changed to 0.0.0.0.

If I did the get request and set the REMOTE_ADDR to 127.0.0.1 all was well!

response = response.goto(url('home'), extra_environ=dict(REMOTE_ADDR='127.0.0.1'))
rootsmith