Skip to content

Commit

Permalink
render Unauthorized and Redirect exceptions in HTTPExceptionHandler m…
Browse files Browse the repository at this point in the history
…iddleware, not in WSGIPublisher
  • Loading branch information
davisagli committed Jan 31, 2017
1 parent e9a418e commit e3f0e3e
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 39 deletions.
1 change: 1 addition & 0 deletions buildout.cfg
Expand Up @@ -19,6 +19,7 @@ parts =
requirements
sources-dir = develop
auto-checkout =
zExceptions


[test]
Expand Down
2 changes: 1 addition & 1 deletion sources.cfg
Expand Up @@ -14,7 +14,7 @@ five.globalrequest = git ${remotes:github}/five.globalrequest pushurl=${remotes:
MultiMapping = git ${remotes:github}/MultiMapping pushurl=${remotes:github_push}/MultiMapping
Persistence = git ${remotes:github}/Persistence pushurl=${remotes:github_push}/Persistence
RestrictedPython = git ${remotes:github}/RestrictedPython pushurl=${remotes:github_push}/RestrictedPython
zExceptions = git ${remotes:github}/zExceptions pushurl=${remotes:github_push}/zExceptions
zExceptions = git ${remotes:github}/zExceptions pushurl=${remotes:github_push}/zExceptions branch=davisagli-exception-headers
zope.globalrequest = git ${remotes:github}/zope.globalrequest pushurl=${remotes:github_push}/zope.globalrequest

# Optional dependencies
Expand Down
4 changes: 4 additions & 0 deletions src/Testing/ZopeTestCase/functional.py
Expand Up @@ -103,6 +103,10 @@ def publish(self, path, basic=None, env=None, extra=None,
wsgi_headers = BytesIO()

def start_response(status, headers):
response.setStatus(status.split()[0])
for name, value in headers:
response.setHeader(name, value)

wsgi_headers.write('HTTP/1.1 %s\r\n' % status)
headers = '\r\n'.join([': '.join(x) for x in headers])
wsgi_headers.write(headers)
Expand Down
1 change: 0 additions & 1 deletion src/Testing/ZopeTestCase/zopedoctest/FunctionalDocTest.txt
Expand Up @@ -99,7 +99,6 @@ Test Unauthorized
... GET /test_folder_1_/index_html HTTP/1.1
... """, handle_errors=True))
HTTP/1.1 401 Unauthorized
...
WWW-Authenticate: basic realm=...

Test Basic Authentication
Expand Down
17 changes: 6 additions & 11 deletions src/ZPublisher/HTTPResponse.py
Expand Up @@ -998,17 +998,14 @@ def badRequestError(self, name):
'and try the request again.</p>' % name)
raise exc

def _unauthorized(self, exc=None):
# This should be handled by zExceptions
status = exc.getStatus() if exc is not None else 401
self.setStatus(status)
def _unauthorized(self, exc):
if self.realm:
self.setHeader('WWW-Authenticate',
'basic realm="%s"' % self.realm, 1)
exc.setHeader('WWW-Authenticate', 'basic realm="%s"' % self.realm)

def unauthorized(self):
exc = Unauthorized()
exc.title = 'You are not authorized to access this resource.'
message = 'You are not authorized to access this resource.'
exc = Unauthorized(message)
exc.title = message
if self.debug_mode:
if self._auth:
exc.detail = 'Username and password are not correct.'
Expand All @@ -1017,9 +1014,7 @@ def unauthorized(self):
raise exc

def _redirect(self, exc):
# This should be handled by zExceptions
self.setStatus(exc.getStatus())
self.setHeader('Location', str(exc))
exc.setHeader('Location', str(exc))

def redirect(self, location, status=302, lock=0):
"""Cause a redirection."""
Expand Down
25 changes: 14 additions & 11 deletions src/ZPublisher/WSGIPublisher.py
Expand Up @@ -174,17 +174,20 @@ def _publish_response(request, response, module_info, _publish=publish):
elif isinstance(exc, Unauthorized):
response._unauthorized(exc)

view = queryMultiAdapter((exc, request), name=u'index.html')
if view is not None:
parents = request.get('PARENTS')
if parents:
view.__parent__ = parents[0]
response.setStatus(exc.__class__)
response.setBody(view())
return response

if isinstance(exc, (HTTPRedirection, Unauthorized)):
return response
# Render exception views unless we are called by the test browser
# with handleErrors = False
if request.environ.get('wsgi.handleErrors', True):
view = queryMultiAdapter((exc, request), name=u'index.html')
if view is not None:
parents = request.get('PARENTS')
if parents:
view.__parent__ = parents[0]
response.setStatus(exc.__class__)
if hasattr(exc, 'headers'):
for name, value in exc.headers.items():
response.setHeader(name, value)
response.setBody(view())
return response

raise

Expand Down
25 changes: 10 additions & 15 deletions src/ZPublisher/tests/test_WSGIPublisher.py
Expand Up @@ -296,32 +296,27 @@ def test_publish_can_return_new_response(self):
self.assertEqual(_after1._called_with, ((), {}))
self.assertEqual(_after2._called_with, ((), {}))

def test_swallows_Unauthorized(self):
def test_raises_annotated_Unauthorized(self):
from zExceptions import Unauthorized
environ = self._makeEnviron()
start_response = DummyCallable()
_publish = DummyCallable()
_publish._raise = Unauthorized('TESTING')
app_iter = self._callFUT(environ, start_response, _publish)
self.assertEqual(app_iter, ('', ''))
(status, headers), kw = start_response._called_with
self.assertEqual(status, '401 Unauthorized')
self.assertTrue(('Content-Length', '0') in headers)
self.assertEqual(kw, {})
try:
self._callFUT(environ, start_response, _publish)
except Exception as e:
self.assertEqual(e.headers['WWW-Authenticate'], 'basic realm="Zope"')

def test_swallows_Redirect(self):
def test_raises_annotated_Redirect(self):
from zExceptions import Redirect
environ = self._makeEnviron()
start_response = DummyCallable()
_publish = DummyCallable()
_publish._raise = Redirect('/redirect_to')
app_iter = self._callFUT(environ, start_response, _publish)
self.assertEqual(app_iter, ('', ''))
(status, headers), kw = start_response._called_with
self.assertEqual(status, '302 Found')
self.assertTrue(('Location', '/redirect_to') in headers)
self.assertTrue(('Content-Length', '0') in headers)
self.assertEqual(kw, {})
try:
self._callFUT(environ, start_response, _publish)
except Exception as e:
self.assertEqual(e.headers['Location'], '/redirect_to')

def test_response_body_is_file(self):
from io import BytesIO
Expand Down
1 change: 1 addition & 0 deletions src/ZPublisher/tests/test_pubevents.py
Expand Up @@ -180,6 +180,7 @@ class _Request(BaseRequest):
response = WSGIResponse()
_hacked_path = False
args = ()
environ = {}

def __init__(self, *args, **kw):
BaseRequest.__init__(self, *args, **kw)
Expand Down

0 comments on commit e3f0e3e

Please sign in to comment.