[Checkins] SVN: zope.testbrowser/branches/jinty-webtest3/src/zope/testbrowser/ Ressurect the AuthorizationMiddleware from trunk, write tests for it and work around a weird bug where unicode headers are getting passed to cStringIO

Brian Sutherland jinty at web.de
Mon Mar 7 05:26:27 EST 2011


Log message for revision 120775:
  Ressurect the AuthorizationMiddleware from trunk, write tests for it and work around a weird bug where unicode headers are getting passed to cStringIO

Changed:
  U   zope.testbrowser/branches/jinty-webtest3/src/zope/testbrowser/ftests/wsgitestapp.py
  U   zope.testbrowser/branches/jinty-webtest3/src/zope/testbrowser/tests/test_wsgi.py
  U   zope.testbrowser/branches/jinty-webtest3/src/zope/testbrowser/wsgi.py

-=-
Modified: zope.testbrowser/branches/jinty-webtest3/src/zope/testbrowser/ftests/wsgitestapp.py
===================================================================
--- zope.testbrowser/branches/jinty-webtest3/src/zope/testbrowser/ftests/wsgitestapp.py	2011-03-07 10:24:42 UTC (rev 120774)
+++ zope.testbrowser/branches/jinty-webtest3/src/zope/testbrowser/ftests/wsgitestapp.py	2011-03-07 10:26:26 UTC (rev 120775)
@@ -39,6 +39,7 @@
         handler = {'/set_status.html': set_status,
                    '/echo.html': echo,
                    '/echo_one.html': echo_one,
+                   '/set_header.html': set_header,
                    '/set_cookie.html': set_cookie,
                    '/get_cookie.html': get_cookie,
                    '/inner/set_cookie.html': set_cookie,
@@ -91,6 +92,15 @@
     resp.set_cookie(name, value, **cookie_parms)
     return resp
 
+def set_header(req):
+    resp = Response()
+    body = [u"Set Headers:"]
+    for k, v in sorted(req.params.items()):
+        body.extend([k, v]) 
+        resp.headers.add(k, v)
+    resp.unicode_body = u'\n'.join(body)
+    return resp
+
 _interesting_environ = ('CONTENT_LENGTH',
                         'CONTENT_TYPE',
                         'HTTP_ACCEPT_LANGUAGE',

Modified: zope.testbrowser/branches/jinty-webtest3/src/zope/testbrowser/tests/test_wsgi.py
===================================================================
--- zope.testbrowser/branches/jinty-webtest3/src/zope/testbrowser/tests/test_wsgi.py	2011-03-07 10:24:42 UTC (rev 120774)
+++ zope.testbrowser/branches/jinty-webtest3/src/zope/testbrowser/tests/test_wsgi.py	2011-03-07 10:26:26 UTC (rev 120775)
@@ -16,6 +16,7 @@
 from wsgiref.simple_server import demo_app
 
 import zope.testbrowser.wsgi
+from zope.testbrowser.ftests.wsgitestapp import WSGITestApplication
 
 
 class SimpleLayer(zope.testbrowser.wsgi.Layer):
@@ -50,3 +51,36 @@
         another_layer = SimpleLayer()
         # The layer has a .app property where the application under test is available
         self.assertRaises(AssertionError, another_layer.setUp)
+
+class TestAuthorizationMiddleware(unittest.TestCase):
+
+    def setUp(self):
+        app = WSGITestApplication()
+        self.unwrapped_browser = zope.testbrowser.wsgi.Browser(wsgi_app=app)
+        app = zope.testbrowser.wsgi.AuthorizationMiddleware(app)
+        self.browser = zope.testbrowser.wsgi.Browser(wsgi_app=app)
+
+    def test_unwanted_headers(self):
+        #x-powered-by and x-content-type-warning are filtered
+        url = 'http://localhost/set_header.html?x-other=another&x-powered-by=zope&x-content-type-warning=bar'
+        self.browser.open(url)
+        self.assertEquals(self.browser.headers['x-other'], 'another')
+        self.assertTrue('x-other' in self.browser.headers)
+        self.assertFalse('x-powered-by' in self.browser.headers)
+        self.assertFalse('x-content-type-warning' in self.browser.headers)
+        # make sure we are actually testing something
+        self.unwrapped_browser.open(url)
+        self.assertTrue('x-powered-by' in self.unwrapped_browser.headers)
+        self.assertTrue('x-content-type-warning' in self.unwrapped_browser.headers)
+    
+    def test_authorization(self):
+        # Basic authorization headers are encoded in base64
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.browser.open('http://localhost/echo_one.html?var=HTTP_AUTHORIZATION')
+        self.assertEquals(self.browser.contents, repr('Basic bWdyOm1ncnB3'))
+
+    def test_authorization_other(self):
+        # Non-Basic authorization headers are unmolested
+        self.browser.addHeader('Authorization', 'Digest foobar')
+        self.browser.open('http://localhost/echo_one.html?var=HTTP_AUTHORIZATION')
+        self.assertEquals(self.browser.contents, repr('Digest foobar'))

Modified: zope.testbrowser/branches/jinty-webtest3/src/zope/testbrowser/wsgi.py
===================================================================
--- zope.testbrowser/branches/jinty-webtest3/src/zope/testbrowser/wsgi.py	2011-03-07 10:24:42 UTC (rev 120774)
+++ zope.testbrowser/branches/jinty-webtest3/src/zope/testbrowser/wsgi.py	2011-03-07 10:26:26 UTC (rev 120775)
@@ -14,6 +14,8 @@
 """WSGI-specific testing code
 """
 
+import base64
+import re
 import sys
 
 from webtest import TestApp
@@ -103,6 +105,10 @@
         headers.sort()
         headers.insert(0, ('Status', response.status))
         headers = '\r\n'.join('%s: %s' % h for h in headers)
+        # Ugh! WebTest's headers can at times be unicode. That causes weird
+        # problems later when they are shoved into a StringIO. So just cast
+        # to a string for now using ascii.
+        headers = str(headers)
         content = response.body
         return zope.testbrowser.connection.Response(content, headers, status, reason)
 
@@ -145,6 +151,59 @@
 
 _APP_UNDER_TEST = None # setup and torn down by the Layer class
 
+# Compatibility helpers to behave like zope.app.testing
+
+basicre = re.compile('Basic (.+)?:(.+)?$')
+
+
+def auth_header(header):
+    """This function takes an authorization HTTP header and encode the
+    couple user, password into base 64 like the HTTP protocol wants
+    it.
+    """
+    match = basicre.match(header)
+    if match:
+        u, p = match.group(1, 2)
+        if u is None:
+            u = ''
+        if p is None:
+            p = ''
+        auth = base64.encodestring('%s:%s' % (u, p))
+        return 'Basic %s' % auth[:-1]
+    return header
+
+
+def is_wanted_header(header):
+    """Return True if the given HTTP header key is wanted.
+    """
+    key, value = header
+    return key.lower() not in ('x-content-type-warning', 'x-powered-by')
+
+
+class AuthorizationMiddleware(object):
+    """This middleware makes the WSGI application compatible with the
+    HTTPCaller behavior defined in zope.app.testing.functional:
+    - It modifies the HTTP Authorization header to encode user and
+      password into base64 if it is Basic authentication.
+    """
+
+    def __init__(self, wsgi_stack):
+        self.wsgi_stack = wsgi_stack
+
+    def __call__(self, environ, start_response):
+        # Handle authorization
+        auth_key = 'HTTP_AUTHORIZATION'
+        if auth_key in environ:
+            environ[auth_key] = auth_header(environ[auth_key])
+
+        # Remove unwanted headers
+        def application_start_response(status, headers, exc_info=None):
+            headers = filter(is_wanted_header, headers)
+            start_response(status, headers)
+
+        for entry in self.wsgi_stack(environ, application_start_response):
+            yield entry
+
 class Layer(object):
     """Test layer which sets up WSGI application for use with
     WebTest/testbrowser.



More information about the checkins mailing list