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
- AccessControl: the form behind the "Security" tab has a new form
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
- Collector #1306: Missing acquisition context on local roles screen.
......
......@@ -13,6 +13,7 @@
"""Initialize the Zope2 Package and provide a published module
"""
from zope.component import queryMultiAdapter
from AccessControl.SecurityManagement import getSecurityManager
from AccessControl.SecurityManagement import newSecurityManager
from AccessControl.SecurityManagement import noSecurityManager
......@@ -20,7 +21,7 @@ from Acquisition import aq_acquire
from App.config import getConfiguration
from time import asctime
from types import StringType, ListType
from zExceptions import Unauthorized
from zExceptions import Unauthorized, Redirect
from ZODB.POSException import ConflictError
import transaction
import AccessControl.User
......@@ -37,6 +38,8 @@ import App.ZApplication
import Zope2
import ZPublisher
app = None
startup_time = asctime()
def startup():
global app
......@@ -138,35 +141,45 @@ def validated_hook(request, user):
class RequestContainer(ExtensionClass.Base):
def __init__(self,r): self.REQUEST=r
conflict_errors = 0
unresolved_conflict_errors = 0
conflict_logger = logging.getLogger('ZPublisher.Conflict')
class ZPublisherExceptionHook:
def __init__(self):
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 zpublisher_exception_hook(published, REQUEST, t, v, traceback):
global unresolved_conflict_errors
global conflict_errors
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:
if t is SystemExit or t is Redirect:
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)
self.logConflicts(v, REQUEST)
raise ZPublisher.Retry(t, v, traceback)
if t is ZPublisher.Retry:
try:
v.reraise()
......@@ -177,7 +190,7 @@ def zpublisher_exception_hook(published, REQUEST, t, v, traceback):
t, v, traceback = sys.exc_info()
if issubclass(t, ConflictError):
# ouch, a user saw this conflict error :-(
unresolved_conflict_errors += 1
self.unresolved_conflict_errors += 1
try:
log = aq_acquire(published, '__error_log__', containment=1)
......@@ -190,32 +203,45 @@ def zpublisher_exception_hook(published, REQUEST, t, v, traceback):
!='text/html'):
raise t, v, traceback
# Lookup a view for the exception and render it, then
# 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)
published = getattr(published, 'im_self', published)
while 1:
f=getattr(published, 'raise_standardErrorMessage', None)
f = getattr(published, self.raise_error_message, None)
if f is None:
published=getattr(published, 'aq_parent', None)
published = getattr(published, 'aq_parent', None)
if published is None:
raise t, v, traceback
else:
break
client=published
client = published
while 1:
if getattr(client, 'standard_error_message', None) is not None:
if getattr(client, self.error_message, None) is not None:
break
client=getattr(client, 'aq_parent', None)
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
REQUEST['AUTHENTICATED_USER'] = AccessControl.User.nobody
try:
f(client, REQUEST, t, v, traceback, error_log_url=error_log_url)
......@@ -224,8 +250,9 @@ def zpublisher_exception_hook(published, REQUEST, t, v, traceback):
f(client, REQUEST, t, v, traceback)
finally:
traceback=None
traceback = None
zpublisher_exception_hook = ZPublisherExceptionHook()
ac_logger = logging.getLogger('event.AccessControl')
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$
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):
pass
implements(IException)
class InternalError(Exception):
pass
implements(IException)
class NotFound(Exception):
pass
implements(INotFound)
class Forbidden(Exception):
pass
implements(IForbidden)
class MethodNotAllowed(Exception):
pass
......
......@@ -15,9 +15,13 @@ $Id$
"""
from types import StringType
from zope.interface import implements
from zope.security.interfaces import IUnauthorized
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):
"""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