Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into py3
Browse files Browse the repository at this point in the history
  • Loading branch information
hannosch committed May 5, 2017
2 parents 68d1fab + 9789d9b commit eaf223a
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 34 deletions.
4 changes: 4 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ https://zope.readthedocs.io/en/2.13/CHANGES.html
Bugs Fixed
++++++++++

- Restore a `_unauthorized` hook on the response object.

- Restore `HTTPResponse.redirect` behaviour of not raising an exception.

Features Added
++++++++++++++

Expand Down
6 changes: 3 additions & 3 deletions requirements-full.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
-e git+https://github.com/zopefoundation/Zope.git@master#egg=Zope2
AccessControl==4.0a4
AccessControl==4.0a5
Acquisition==4.4.1
AuthEncoding==4.0.0
BTrees==4.4.1
Chameleon==2.25
DateTime==4.2
DocumentTemplate==3.0a1
DocumentTemplate==3.0a2
ExtensionClass==4.3.0
Missing==3.2
MultiMapping==3.1
Expand All @@ -16,7 +16,7 @@ Products.MailHost==3.0
Products.ZCTextIndex==4.0.1
Products.ZCatalog==4.0a3
Record==3.2
RestrictedPython==3.6.0
RestrictedPython==4.0a1
WSGIProxy2==0.4.3
WebOb==1.7.2
WebTest==2.0.27
Expand Down
1 change: 1 addition & 0 deletions src/Testing/ZopeTestCase/zopedoctest/FunctionalDocTest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ 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
36 changes: 36 additions & 0 deletions src/Testing/tests/test_testbrowser.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ def __call__(self, REQUEST):
raise ValueError('dummy')


class RedirectStub(Item):
"""This is a stub, causing a redirect."""

def __call__(self, REQUEST):
return REQUEST.RESPONSE.redirect('/redirected')


class TestTestbrowser(FunctionalTestCase):

def test_auth(self):
Expand Down Expand Up @@ -86,6 +93,7 @@ def test_cookies(self):
def test_handle_errors_true(self):
self.folder._setObject('stub', ExceptionStub())
browser = Browser()

with self.assertRaises(HTTPError):
browser.open('http://localhost/test_folder_1_/stub')
self.assertTrue(browser.headers['status'].startswith('500'))
Expand All @@ -94,10 +102,20 @@ def test_handle_errors_true(self):
browser.open('http://localhost/nothing-is-here')
self.assertTrue(browser.headers['status'].startswith('404'))

def test_handle_errors_true_redirect(self):
self.folder._setObject('redirect', RedirectStub())
browser = Browser()

with self.assertRaises(HTTPError):
browser.open('http://localhost/test_folder_1_/redirect')
self.assertTrue(browser.headers['status'].startswith('404'))
self.assertEqual(browser.url, 'http://localhost/redirected')

def test_handle_errors_false(self):
self.folder._setObject('stub', ExceptionStub())
browser = Browser()
browser.handleErrors = False

with self.assertRaises(ValueError):
browser.open('http://localhost/test_folder_1_/stub')
self.assertTrue(browser.contents is None)
Expand All @@ -106,6 +124,15 @@ def test_handle_errors_false(self):
browser.open('http://localhost/nothing-is-here')
self.assertTrue(browser.contents is None)

def test_handle_errors_false_redirect(self):
self.folder._setObject('redirect', RedirectStub())
browser = Browser()
browser.handleErrors = False

with self.assertRaises(NotFound):
browser.open('http://localhost/test_folder_1_/redirect')
self.assertTrue(browser.contents is None)

def test_raise_http_errors_false(self):
self.folder._setObject('stub', ExceptionStub())
browser = Browser()
Expand All @@ -117,6 +144,15 @@ def test_raise_http_errors_false(self):
browser.open('http://localhost/nothing-is-here')
self.assertTrue(browser.headers['status'].startswith('404'))

def test_raise_http_errors_false_redirect(self):
self.folder._setObject('redirect', RedirectStub())
browser = Browser()
browser.raiseHttpErrors = False

browser.open('http://localhost/test_folder_1_/redirect')
self.assertTrue(browser.headers['status'].startswith('404'))
self.assertEqual(browser.url, 'http://localhost/redirected')

def test_headers_camel_case(self):
# The Zope2 response mungs headers so they come out
# in camel case. We should do the same.
Expand Down
40 changes: 17 additions & 23 deletions src/ZPublisher/HTTPResponse.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,15 @@ def __init__(self,
self.stdout = stdout
self.stderr = stderr

def redirect(self, location, status=302, lock=0):
"""Cause a redirection without raising an error"""
if isinstance(location, HTTPRedirection):
status = location.getStatus()
location = str(location)
self.setStatus(status, lock=lock)
self.setHeader('Location', location)
return location

def retry(self):
""" Return a cloned response object to be used in a retry attempt.
"""
Expand Down Expand Up @@ -670,6 +679,11 @@ def listHeaders(self):
result.extend(self.accumulated_headers)
return result

def _unauthorized(self):
realm = self.realm
if realm:
self.setHeader('WWW-Authenticate', 'basic realm="%s"' % realm, 1)


class HTTPResponse(HTTPBaseResponse):

Expand Down Expand Up @@ -780,11 +794,6 @@ def badRequestError(self, name):
"and try the request again.</p>"
))

def _unauthorized(self):
realm = self.realm
if realm:
self.setHeader('WWW-Authenticate', 'basic realm="%s"' % realm, 1)

def unauthorized(self):
m = "You are not authorized to access this resource."
if self.debug_mode:
Expand All @@ -794,15 +803,6 @@ def unauthorized(self):
m = m + '\nNo Authorization header found.'
raise Unauthorized(m)

def redirect(self, location, status=302, lock=0):
"""Cause a redirection without raising an error"""
if isinstance(location, HTTPRedirection):
status = location.getStatus()
location = str(location)
self.setStatus(status, lock=lock)
self.setHeader('Location', location)
return location

def _setBCIHeaders(self, t, tb):
try:
# Try to capture exception info for bci calls
Expand Down Expand Up @@ -1005,21 +1005,15 @@ def unauthorized(self):
exc.detail = 'No Authorization header found.'
raise exc

def redirect(self, location, status=302, lock=0):
"""Cause a redirection."""
if isinstance(location, HTTPRedirection):
raise location

exc = Redirect(str(location))
exc.setStatus(status)
raise exc

def exception(self, fatal=0, info=None, abort=1):
if isinstance(info, tuple) and len(info) == 3:
t, v, tb = info
else:
t, v, tb = sys.exc_info()

if issubclass(t, Unauthorized):
self._unauthorized()

reraise(t, v, tb)

def finalize(self):
Expand Down
10 changes: 6 additions & 4 deletions src/ZPublisher/WSGIPublisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,13 @@ def _publish_response(request, response, module_info, _publish=publish):
if not isinstance(exc, t):
exc = t(str(exc))

# This should happen inside zExceptions, but the realm is only
# defined on the premade response or in the module_info and
# can be changed during publishing.
if isinstance(exc, Unauthorized):
exc.setRealm(response.realm)
# _unauthorized modifies the response in-place in unknown
# ways, so we have to trust and return it, and ignore
# the exception instance.
response._unauthorized()
response.setStatus(exc.getStatus())
return response

view = queryMultiAdapter((exc, request), name=u'index.html')
if view is not None:
Expand Down
11 changes: 10 additions & 1 deletion src/ZPublisher/tests/test_WSGIPublisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,15 @@ def test___str___raises(self):
response.setBody('TESTING')
self.assertRaises(NotImplementedError, lambda: str(response))

def test_exception_calls_unauthorized(self):
from zExceptions import Unauthorized
response = self._makeOne()
_unauthorized = DummyCallable()
response._unauthorized = _unauthorized
with self.assertRaises(Unauthorized):
response.exception(info=(Unauthorized, Unauthorized('fail'), None))
self.assertEqual(_unauthorized._called_with, ((), {}))


class TestPublish(unittest.TestCase):

Expand Down Expand Up @@ -439,7 +448,7 @@ def testCustomExceptionViewUnauthorized(self):
app_iter = self._callFUT(environ, start_response, _publish)
body = b''.join(app_iter)
self.assertEqual(start_response._called_with[0][0], '401 Unauthorized')
self.assertTrue(b'Exception View: Unauthorized' in body)
self.assertFalse(b'Exception View' in body)

def testCustomExceptionViewForbidden(self):
from zExceptions import Forbidden
Expand Down
6 changes: 3 additions & 3 deletions versions-prod.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

[versions]
Zope2 =
AccessControl = 4.0a4
AccessControl = 4.0a5
AuthEncoding = 4.0.0
Acquisition = 4.4.1
BTrees = 4.4.1
Chameleon = 2.25
DateTime = 4.2
DocumentTemplate = 3.0a1
DocumentTemplate = 3.0a2
ExtensionClass = 4.3.0
five.globalrequest = 1.0
funcsigs = 1.0.2
Expand All @@ -25,7 +25,7 @@ Products.ZCatalog = 4.0a3
Products.ZCTextIndex = 4.0.1
pytz = 2017.2
Record = 3.2
RestrictedPython = 3.6.0
RestrictedPython = 4.0a1
six = 1.10.0
transaction = 2.1.2
waitress = 1.0.2
Expand Down

0 comments on commit eaf223a

Please sign in to comment.