Commit f52d26b7 authored by Martijn Faassen's avatar Martijn Faassen

Integrate Zope 3-based exception views. Patch by Sidnei, integration

work done for Infrae.
parent 3494ef44
...@@ -100,6 +100,27 @@ Zope Changes ...@@ -100,6 +100,27 @@ Zope Changes
- AccessControl: the form behind the "Security" tab has a new form - AccessControl: the form behind the "Security" tab has a new form
for user-related reporting of permissions and roles for user-related reporting of permissions and roles
- Zope 3-based exception views can now be registered in ZCML for
various exceptions that can be raised by Zope. Registering an
exception view can be done like this:
<browser:page
for="zope.publisher.interfaces.INotFound"
class=".view.SomeView"
name="index.html"
permission="zope.Public" />
Relevant exceptions that can have views are:
zope.interface.common.interfaces.IException
zope.publisher.interfaces.INotFound
zope.security.interfaces.IForbidden
zope.security.interfaces.IUnauthorized
Note that the name has to be 'index.html' for the exception
view to work. (patch by Sidnei da Silva from Enfold,
integration by Martijn Faassen (Startifact) for Infrae)
Bugs Fixed Bugs Fixed
- Collector #1306: Missing acquisition context on local roles screen. - Collector #1306: Missing acquisition context on local roles screen.
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
"""Initialize the Zope2 Package and provide a published module """Initialize the Zope2 Package and provide a published module
""" """
from zope.component import queryMultiAdapter
from AccessControl.SecurityManagement import getSecurityManager from AccessControl.SecurityManagement import getSecurityManager
from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManagement import newSecurityManager
from AccessControl.SecurityManagement import noSecurityManager from AccessControl.SecurityManagement import noSecurityManager
...@@ -20,7 +21,7 @@ from Acquisition import aq_acquire ...@@ -20,7 +21,7 @@ from Acquisition import aq_acquire
from App.config import getConfiguration from App.config import getConfiguration
from time import asctime from time import asctime
from types import StringType, ListType from types import StringType, ListType
from zExceptions import Unauthorized from zExceptions import Unauthorized, Redirect
from ZODB.POSException import ConflictError from ZODB.POSException import ConflictError
import transaction import transaction
import AccessControl.User import AccessControl.User
...@@ -37,6 +38,8 @@ import App.ZApplication ...@@ -37,6 +38,8 @@ import App.ZApplication
import Zope2 import Zope2
import ZPublisher import ZPublisher
app = None
startup_time = asctime()
def startup(): def startup():
global app global app
...@@ -132,100 +135,124 @@ def validated_hook(request, user): ...@@ -132,100 +135,124 @@ def validated_hook(request, user):
) )
Zope2.DB.removeVersionPool(version) Zope2.DB.removeVersionPool(version)
raise Unauthorized, "You don't have permission to enter versions." raise Unauthorized, "You don't have permission to enter versions."
class RequestContainer(ExtensionClass.Base): class RequestContainer(ExtensionClass.Base):
def __init__(self,r): self.REQUEST=r def __init__(self,r): self.REQUEST=r
conflict_errors = 0 class ZPublisherExceptionHook:
unresolved_conflict_errors = 0
def __init__(self):
conflict_logger = logging.getLogger('ZPublisher.Conflict') self.conflict_errors = 0
self.unresolved_conflict_errors = 0
self.conflict_logger = logging.getLogger('ZPublisher.Conflict')
self.error_message = 'standard_error_message'
self.raise_error_message = 'raise_standardErrorMessage'
def logConflicts(self, v, REQUEST):
self.conflict_errors += 1
level = getattr(getConfiguration(), 'conflict_error_log_level', 0)
if not self.conflict_logger.isEnabledFor(level):
return False
self.conflict_logger.log(
level,
"%s at %s: %s (%d conflicts (%d unresolved) "
"since startup at %s)",
v.__class__.__name__,
REQUEST.get('PATH_INFO', '<unknown>'),
v,
self.conflict_errors,
self.unresolved_conflict_errors,
startup_time)
return True
def __call__(self, published, REQUEST, t, v, traceback):
try:
if isinstance(t, StringType):
if t.lower() in ('unauthorized', 'redirect'):
raise
else:
if t is SystemExit or t is Redirect:
raise
def zpublisher_exception_hook(published, REQUEST, t, v, traceback):
global unresolved_conflict_errors
global conflict_errors
try:
if isinstance(t, StringType):
if t.lower() in ('unauthorized', 'redirect'):
raise
else:
if t is SystemExit:
raise
if issubclass(t, ConflictError):
conflict_errors += 1
level = getConfiguration().conflict_error_log_level
if level:
conflict_logger.log(level,
"%s at %s: %s (%d conflicts (%d unresolved) "
"since startup at %s)",
v.__class__.__name__,
REQUEST.get('PATH_INFO', '<unknown>'),
v,
conflict_errors,
unresolved_conflict_errors,
startup_time)
raise ZPublisher.Retry(t, v, traceback)
if t is ZPublisher.Retry:
try:
v.reraise()
except:
# we catch the re-raised exception so that it gets
# stored in the error log and gets rendered with
# standard_error_message
t, v, traceback = sys.exc_info()
if issubclass(t, ConflictError): if issubclass(t, ConflictError):
# ouch, a user saw this conflict error :-( self.logConflicts(v, REQUEST)
unresolved_conflict_errors += 1 raise ZPublisher.Retry(t, v, traceback)
if t is ZPublisher.Retry:
try:
v.reraise()
except:
# we catch the re-raised exception so that it gets
# stored in the error log and gets rendered with
# standard_error_message
t, v, traceback = sys.exc_info()
if issubclass(t, ConflictError):
# ouch, a user saw this conflict error :-(
self.unresolved_conflict_errors += 1
try: try:
log = aq_acquire(published, '__error_log__', containment=1) log = aq_acquire(published, '__error_log__', containment=1)
except AttributeError: except AttributeError:
error_log_url = '' error_log_url = ''
else:
error_log_url = log.raising((t, v, traceback))
if (getattr(REQUEST.get('RESPONSE', None), '_error_format', '')
!='text/html'):
raise t, v, traceback
if (published is None or published is app or
type(published) is ListType):
# At least get the top-level object
published=app.__bobo_traverse__(REQUEST).__of__(
RequestContainer(REQUEST))
published=getattr(published, 'im_self', published)
while 1:
f=getattr(published, 'raise_standardErrorMessage', None)
if f is None:
published=getattr(published, 'aq_parent', None)
if published is None:
raise t, v, traceback
else: else:
break error_log_url = log.raising((t, v, traceback))
client=published if (getattr(REQUEST.get('RESPONSE', None), '_error_format', '')
while 1: !='text/html'):
if getattr(client, 'standard_error_message', None) is not None:
break
client=getattr(client, 'aq_parent', None)
if client is None:
raise t, v, traceback raise t, v, traceback
if REQUEST.get('AUTHENTICATED_USER', None) is None: # Lookup a view for the exception and render it, then
REQUEST['AUTHENTICATED_USER']=AccessControl.User.nobody # raise the rendered value as the exception value
# (basically the same that 'raise_standardErrorMessage'
# does. The view is named 'index.html' because that's what
# Zope 3 uses as well.
view = queryMultiAdapter((v, REQUEST), name=u'index.html')
if view is not None:
v = view()
response = REQUEST.RESPONSE
response.setStatus(t)
response.setBody(v)
return response
if (published is None or published is app or
type(published) is ListType):
# At least get the top-level object
published=app.__bobo_traverse__(REQUEST).__of__(
RequestContainer(REQUEST))
published = getattr(published, 'im_self', published)
while 1:
f = getattr(published, self.raise_error_message, None)
if f is None:
published = getattr(published, 'aq_parent', None)
if published is None:
raise t, v, traceback
else:
break
try: client = published
f(client, REQUEST, t, v, traceback, error_log_url=error_log_url) while 1:
except TypeError: if getattr(client, self.error_message, None) is not None:
# Pre 2.6 call signature break
f(client, REQUEST, t, v, traceback) client = getattr(client, 'aq_parent', None)
if client is None:
raise t, v, traceback
if REQUEST.get('AUTHENTICATED_USER', None) is None:
REQUEST['AUTHENTICATED_USER'] = AccessControl.User.nobody
try:
f(client, REQUEST, t, v, traceback, error_log_url=error_log_url)
except TypeError:
# Pre 2.6 call signature
f(client, REQUEST, t, v, traceback)
finally: finally:
traceback=None traceback = None
zpublisher_exception_hook = ZPublisherExceptionHook()
ac_logger = logging.getLogger('event.AccessControl') ac_logger = logging.getLogger('event.AccessControl')
class TransactionsManager: class TransactionsManager:
......
##############################################################################
#
# Copyright (c) 2007 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Tests of the Zope2.App package."""
This diff is collapsed.
...@@ -20,17 +20,22 @@ $Id$ ...@@ -20,17 +20,22 @@ $Id$
from unauthorized import Unauthorized from unauthorized import Unauthorized
from zope.interface import implements
from zope.interface.common.interfaces import IException
from zope.publisher.interfaces import INotFound
from zope.security.interfaces import IForbidden
class BadRequest(Exception): class BadRequest(Exception):
pass implements(IException)
class InternalError(Exception): class InternalError(Exception):
pass implements(IException)
class NotFound(Exception): class NotFound(Exception):
pass implements(INotFound)
class Forbidden(Exception): class Forbidden(Exception):
pass implements(IForbidden)
class MethodNotAllowed(Exception): class MethodNotAllowed(Exception):
pass pass
......
...@@ -15,9 +15,13 @@ $Id$ ...@@ -15,9 +15,13 @@ $Id$
""" """
from types import StringType from types import StringType
from zope.interface import implements
from zope.security.interfaces import IUnauthorized
class Unauthorized(Exception): class Unauthorized(Exception):
"""Some user wasn't allowed to access a resource""" """Some user wasn't allowed to access a resource
"""
implements(IUnauthorized)
def __init__(self, message=None, value=None, needed=None, name=None, **kw): def __init__(self, message=None, value=None, needed=None, name=None, **kw):
"""Possible signatures: """Possible signatures:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment