Tres Seaver
2011-09-28 493726fee6ed55a97dcd0dd52d9d8064cdcfd762
Call 'close' even when raising an exception for a missing challenger.

See http://bugs.repoze.org/issue174
3 files modified
34 ■■■■■ changed files
CHANGES.txt 8 ●●●●● patch | view | raw | blame | history
repoze/who/middleware.py 6 ●●●● patch | view | raw | blame | history
repoze/who/tests/test_middleware.py 20 ●●●●● patch | view | raw | blame | history
CHANGES.txt
@@ -1,6 +1,14 @@
repoze.who Changelog
====================
2.0 (unreleased)
----------------
- Further harden middleware, calling ``close()`` on the iterable even if
  raising an exception for a missing challenger.
  http://bugs.repoze.org/issue174
2.0b1 (2011-05-24)
------------------
repoze/who/middleware.py
@@ -96,6 +96,7 @@
        if api.challenge_decider(environ, wrapper.status, wrapper.headers):
            logger and logger.info('challenge required')
            close = getattr(app_iter, 'close', _no_op)
            challenge_app = api.challenge(wrapper.status, wrapper.headers)
            if challenge_app is not None:
@@ -104,12 +105,12 @@
                    list(app_iter) # unwind the original app iterator
                # PEP 333 requires that we call the original iterator's
                # 'close' method, if it exists, before releasing it.
                close = getattr(app_iter, 'close', lambda: None)
                close()
                # replace the downstream app with the challenge app
                app_iter = challenge_app(environ, start_response)
            else:
                logger and logger.info('configuration error: no challengers')
                close()
                raise RuntimeError('no challengers found')
        else:
            logger and logger.info('no challenge required')
@@ -119,6 +120,9 @@
        logger and logger.info(_ENDED % path_info)
        return app_iter
def _no_op():
    pass
def wrap_generator(result):
    """\
    This function returns a generator that behaves exactly the same as the
repoze/who/tests/test_middleware.py
@@ -446,6 +446,26 @@
        self.failUnless(result[0].startswith('401 Unauthorized\r\n'))
        self.failUnless(app._iterable._closed)
    def test_call_w_challenge_but_no_challenger_still_closes_iterable(self):
        environ = self._makeEnviron()
        headers = [('a', '1')]
        app = DummyIterableWithCloseApp('401 Unauthorized', headers)
        challengers = []
        credentials = {'login':'chris', 'password':'password'}
        identifier = DummyIdentifier(credentials)
        identifiers = [ ('identifier', identifier) ]
        authenticator = DummyAuthenticator()
        authenticators = [ ('authenticator', authenticator) ]
        mdprovider = DummyMDProvider({'foo':'bar'})
        mdproviders = [ ('mdprovider', mdprovider) ]
        mw = self._makeOne(app=app, challengers=challengers,
                           identifiers=identifiers,
                           authenticators=authenticators,
                           mdproviders=mdproviders)
        start_response = DummyStartResponse()
        self.assertRaises(RuntimeError, mw, environ, start_response)
        self.failUnless(app._iterable._closed)
    # XXX need more call tests:
    #  - auth_id sorting