diff options
| author | Chris McDonough <chrism@agendaless.com> | 2009-09-30 03:27:28 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2009-09-30 03:27:28 +0000 |
| commit | d75fe70228c89e3606e51a4d5775faf549252a90 (patch) | |
| tree | 043a7334d49a7455b80789f1aab56e4238dbd6b0 | |
| parent | 4be6ce73f41e09bf0f3e5df01d7f5aaf4f3137a6 (diff) | |
| download | pyramid-d75fe70228c89e3606e51a4d5775faf549252a90.tar.gz pyramid-d75fe70228c89e3606e51a4d5775faf549252a90.tar.bz2 pyramid-d75fe70228c89e3606e51a4d5775faf549252a90.zip | |
- The import of ``repoze.bfg.view.NotFound`` is deprecated in favor of
``repoze.bfg.exceptions.NotFound``. The old location still
functions, but emits a deprecation warning.
- The import of ``repoze.bfg.security.Unauthorized`` is deprecated in
favor of ``repoze.bfg.exceptions.Forbidden``. The old location
still functions but emits a deprecation warning. The rename from
``Unauthorized`` to ``Forbidden`` brings parity to the the name of
the exception and the system view it invokes when raised.
- New ``repoze.bfg.exceptions`` module was created to house exceptions
that were previously sprinkled through various modules.
- An ``exceptions`` API chapter was added, documenting the new
``repoze.bfg.exceptions`` module.
| -rw-r--r-- | CHANGES.txt | 19 | ||||
| -rw-r--r-- | docs/api/exceptions.rst | 11 | ||||
| -rw-r--r-- | docs/index.rst | 1 | ||||
| -rw-r--r-- | docs/narr/views.rst | 4 | ||||
| -rw-r--r-- | repoze/bfg/exceptions.py | 24 | ||||
| -rw-r--r-- | repoze/bfg/interfaces.py | 6 | ||||
| -rw-r--r-- | repoze/bfg/router.py | 6 | ||||
| -rw-r--r-- | repoze/bfg/security.py | 14 | ||||
| -rw-r--r-- | repoze/bfg/testing.py | 4 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_router.py | 6 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_testing.py | 4 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_view.py | 18 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_zcml.py | 8 | ||||
| -rw-r--r-- | repoze/bfg/view.py | 26 |
14 files changed, 111 insertions, 40 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 347d0292f..981483e25 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,6 +7,9 @@ Documentation - Added a diagram of model graph traversal to the "Traversal" narrative chapter of the documentation. +- An ``exceptions`` API chapter was added, documenting the new + ``repoze.bfg.exceptions`` module. + Features -------- @@ -30,12 +33,28 @@ Features - Minor speedup of ``repoze.bfg.router.Router.__call__``. +- New ``repoze.bfg.exceptions`` module was created to house exceptions + that were previously sprinkled through various modules. + Internal -------- - Move ``repoze.bfg.traversal._url_quote`` into ``repoze.bfg.encode`` as ``url_quote``. +Deprecations +------------ + +- The import of ``repoze.bfg.view.NotFound`` is deprecated in favor of + ``repoze.bfg.exceptions.NotFound``. The old location still + functions, but emits a deprecation warning. + +- The import of ``repoze.bfg.security.Unauthorized`` is deprecated in + favor of ``repoze.bfg.exceptions.Forbidden``. The old location + still functions but emits a deprecation warning. The rename from + ``Unauthorized`` to ``Forbidden`` brings parity to the the name of + the exception and the system view it invokes when raised. + Backwards Incompatibilities --------------------------- diff --git a/docs/api/exceptions.rst b/docs/api/exceptions.rst new file mode 100644 index 000000000..63a3916ae --- /dev/null +++ b/docs/api/exceptions.rst @@ -0,0 +1,11 @@ +.. _exceptions_module: + +:mod:`repoze.bfg.exceptions` +---------------------------- + +.. automodule:: repoze.bfg.exceptions + + .. autoclass:: NotFound + + .. autoclass:: Forbidden + diff --git a/docs/index.rst b/docs/index.rst index 6b85f1440..1305104c4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -57,6 +57,7 @@ Per-module :mod:`repoze.bfg` API documentation. api/chameleon_text api/chameleon_zpt api/events + api/exceptions api/location api/paster api/router diff --git a/docs/narr/views.rst b/docs/narr/views.rst index 5fe66dc63..b45221e71 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -680,7 +680,7 @@ The value of the ``permission`` attribute represents the permission that must be possessed by the user to invoke any found view. When a view is found that matches all predicates, but the invoking user does not possess the permission implied by any associated ``permission`` in -the current context, processing stops, and an ``Unauthorized`` error +the current context, processing stops, and an ``Forbidden`` error is raised, usually resulting in a "forbidden" view being shown to the invoking user. No further view narrowing or view lookup is done. @@ -989,7 +989,7 @@ user does not possess the ``add`` permission relative to the current .. note:: Packages such as :term:`repoze.who` are capable of intercepting an - ``Unauthorized`` response and displaying a form that asks a user to + ``Forbidden`` response and displaying a form that asks a user to authenticate. Use this kind of package to ask the user for authentication credentials. diff --git a/repoze/bfg/exceptions.py b/repoze/bfg/exceptions.py new file mode 100644 index 000000000..afd617a2c --- /dev/null +++ b/repoze/bfg/exceptions.py @@ -0,0 +1,24 @@ +class Forbidden(Exception): + """\ + Raise this exception within :term:`view` code to immediately + return the Forbidden view to the invoking user. Usually this is a + basic ``401`` page, but the Forbidden view can be customized as + necessary. See :ref:`changing_the_forbidden_view`. + + This exception's constructor accepts a single positional argument, + which should be a string. The value of this string will be placed + into the WSGI environment under the ``repoze.bfg.message`` key, + for availability to the Forbidden view.""" + +class NotFound(Exception): + """\ + Raise this exception within :term:`view` code to immediately + return the Not Found view to the invoking user. Usually this is a + basic ``404`` page, but the Not Found view can be customized as + necessary. See :ref:`changing_the_notfound_view`. + + This exception's constructor accepts a single positional argument, + which should be a string. The value of this string will be placed + into the WSGI environment under the ``repoze.bfg.message`` key, + for availability to the Not Found view.""" + diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py index 8896c883e..5d8394484 100644 --- a/repoze/bfg/interfaces.py +++ b/repoze/bfg/interfaces.py @@ -26,8 +26,10 @@ class IResponse(Interface): class IView(Interface): def __call__(context, request): """ Must return an object that implements IResponse. May - optionally raise ``repoze.bfg.security.Unauthorized`` if an - authorization failure is detected during view execution.""" + optionally raise ``repoze.bfg.exceptions.Forbidden`` if an + authorization failure is detected during view execution or + ``repoze.bfg.exceptions.NotFound`` if the not found page is + meant to be returned.""" class ISecuredView(IView): """ *Internal only* interface. Not an API. """ diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py index ec6c5969a..e2b2b6830 100644 --- a/repoze/bfg/router.py +++ b/repoze/bfg/router.py @@ -23,18 +23,18 @@ from repoze.bfg.authorization import ACLAuthorizationPolicy from repoze.bfg.events import NewRequest from repoze.bfg.events import NewResponse from repoze.bfg.events import WSGIApplicationCreatedEvent +from repoze.bfg.exceptions import Forbidden +from repoze.bfg.exceptions import NotFound from repoze.bfg.log import make_stream_logger from repoze.bfg.registry import Registry from repoze.bfg.registry import populateRegistry from repoze.bfg.request import request_factory -from repoze.bfg.security import Unauthorized from repoze.bfg.settings import Settings from repoze.bfg.settings import get_options from repoze.bfg.threadlocal import manager from repoze.bfg.traversal import ModelGraphTraverser from repoze.bfg.traversal import _traverse from repoze.bfg.urldispatch import RoutesRootFactory -from repoze.bfg.view import NotFound from repoze.bfg.view import default_forbidden_view from repoze.bfg.view import default_notfound_view @@ -118,7 +118,7 @@ class Router(object): else: try: response = view_callable(context, request) - except Unauthorized, why: + except Forbidden, why: msg = why[0] environ = getattr(request, 'environ', {}) environ['repoze.bfg.message'] = msg diff --git a/repoze/bfg/security.py b/repoze/bfg/security.py index 466c33da3..e0873a0ad 100644 --- a/repoze/bfg/security.py +++ b/repoze/bfg/security.py @@ -2,10 +2,21 @@ from zope.component import getSiteManager from zope.component import providedBy from zope.component import queryUtility +from zope.deprecation import deprecated + from repoze.bfg.interfaces import IAuthenticationPolicy from repoze.bfg.interfaces import IAuthorizationPolicy from repoze.bfg.interfaces import ISecuredView +# b/c import +from repoze.bfg.exceptions import Forbidden as Unauthorized + +deprecated('Unauthorized', + "('from repoze.bfg.security import Unauthorized' was " + "deprecated as of repoze.bfg 1.1; instead use 'from " + "repoze.bfg.exceptions import Forbidden')", + ) + Everyone = 'system.Everyone' Authenticated = 'system.Authenticated' Allow = 'Allow' @@ -229,6 +240,3 @@ class ACLAllowed(ACLPermitsResult): as the ``msg`` attribute.""" boolval = 1 -class Unauthorized(Exception): - pass - diff --git a/repoze/bfg/testing.py b/repoze/bfg/testing.py index 92bb4399a..2e31afce6 100644 --- a/repoze/bfg/testing.py +++ b/repoze/bfg/testing.py @@ -101,7 +101,7 @@ def registerView(name, result='', view=None, for_=(Interface, Interface), from repoze.bfg.interfaces import IView from repoze.bfg.interfaces import ISecuredView from repoze.bfg.security import has_permission - from repoze.bfg.security import Unauthorized + from repoze.bfg.exceptions import Forbidden if view is None: def view(context, request): from webob import Response @@ -111,7 +111,7 @@ def registerView(name, result='', view=None, for_=(Interface, Interface), else: def _secure(context, request): if not has_permission(permission, context, request): - raise Unauthorized('no permission') + raise Forbidden('no permission') else: return view(context, request) _secure.__call_permissive__ = view diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py index 3116d2572..5c39b65f0 100644 --- a/repoze/bfg/tests/test_router.py +++ b/repoze/bfg/tests/test_router.py @@ -672,10 +672,10 @@ class DummyView: def __call__(self, context, request): if self.raise_unauthorized: - from repoze.bfg.security import Unauthorized - raise Unauthorized('unauthorized') + from repoze.bfg.exceptions import Forbidden + raise Forbidden('unauthorized') if self.raise_notfound: - from repoze.bfg.view import NotFound + from repoze.bfg.exceptions import NotFound raise NotFound('notfound') return self.response diff --git a/repoze/bfg/tests/test_testing.py b/repoze/bfg/tests/test_testing.py index c302bd9f5..938115ecd 100644 --- a/repoze/bfg/tests/test_testing.py +++ b/repoze/bfg/tests/test_testing.py @@ -149,7 +149,7 @@ class TestTestingFunctions(unittest.TestCase): def test_registerView_with_permission_denying(self): from repoze.bfg import testing - from repoze.bfg.security import Unauthorized + from repoze.bfg.exceptions import Forbidden def view(context, request): """ """ view = testing.registerView('moo.html', view=view, permission='bar') @@ -157,7 +157,7 @@ class TestTestingFunctions(unittest.TestCase): import types self.failUnless(isinstance(view, types.FunctionType)) from repoze.bfg.view import render_view_to_response - self.assertRaises(Unauthorized, render_view_to_response, + self.assertRaises(Forbidden, render_view_to_response, None, None, 'moo.html') def test_registerView_with_permission_denying2(self): diff --git a/repoze/bfg/tests/test_view.py b/repoze/bfg/tests/test_view.py index edbfa120f..32c9d391b 100644 --- a/repoze/bfg/tests/test_view.py +++ b/repoze/bfg/tests/test_view.py @@ -467,14 +467,14 @@ class TestMultiView(unittest.TestCase): self.assertEqual(mv.views, [(99, 'view2'), (100, 'view')]) def test_match_not_found(self): - from repoze.bfg.view import NotFound + from repoze.bfg.exceptions import NotFound mv = self._makeOne() context = DummyContext() request = DummyRequest() self.assertRaises(NotFound, mv.match, context, request) def test_match_predicate_fails(self): - from repoze.bfg.view import NotFound + from repoze.bfg.exceptions import NotFound mv = self._makeOne() def view(context, request): """ """ @@ -496,7 +496,7 @@ class TestMultiView(unittest.TestCase): self.assertEqual(result, view) def test_permitted_no_views(self): - from repoze.bfg.view import NotFound + from repoze.bfg.exceptions import NotFound mv = self._makeOne() context = DummyContext() request = DummyRequest() @@ -527,14 +527,14 @@ class TestMultiView(unittest.TestCase): self.assertEqual(result, False) def test__call__not_found(self): - from repoze.bfg.view import NotFound + from repoze.bfg.exceptions import NotFound mv = self._makeOne() context = DummyContext() request = DummyRequest() self.assertRaises(NotFound, mv, context, request) def test___call__intermediate_not_found(self): - from repoze.bfg.view import NotFound + from repoze.bfg.exceptions import NotFound mv = self._makeOne() context = DummyContext() request = DummyRequest() @@ -561,7 +561,7 @@ class TestMultiView(unittest.TestCase): self.assertEqual(response, expected_response) def test__call_permissive__not_found(self): - from repoze.bfg.view import NotFound + from repoze.bfg.exceptions import NotFound mv = self._makeOne() context = DummyContext() request = DummyRequest() @@ -1408,7 +1408,7 @@ class TestDeriveView(unittest.TestCase): "'view_name' against context None): True") def test_view_with_debug_authorization_permission_authpol_denied(self): - from repoze.bfg.security import Unauthorized + from repoze.bfg.exceptions import Forbidden def view(context, request): """ """ self._registerSettings(debug_authorization=True, reload_templates=True) @@ -1422,7 +1422,7 @@ class TestDeriveView(unittest.TestCase): request = DummyRequest() request.view_name = 'view_name' request.url = 'url' - self.assertRaises(Unauthorized, result, None, request) + self.assertRaises(Forbidden, result, None, request) self.assertEqual(len(logger.messages), 1) self.assertEqual(logger.messages[0], "debug_authorization of url url (view name " @@ -1462,7 +1462,7 @@ class TestDeriveView(unittest.TestCase): self.assertEqual(predicates, [True, True]) def test_view_with_predicates_notall(self): - from repoze.bfg.view import NotFound + from repoze.bfg.exceptions import NotFound def view(context, request): """ """ predicates = [] diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py index 89ad71cac..35584f301 100644 --- a/repoze/bfg/tests/test_zcml.py +++ b/repoze/bfg/tests/test_zcml.py @@ -573,7 +573,7 @@ class TestViewDirective(unittest.TestCase): from zope.interface import Interface from repoze.bfg.interfaces import IRequest from repoze.bfg.interfaces import IView - from repoze.bfg.view import NotFound + from repoze.bfg.exceptions import NotFound context = DummyContext() class IFoo(Interface): pass @@ -626,7 +626,7 @@ class TestViewDirective(unittest.TestCase): from zope.interface import Interface from repoze.bfg.interfaces import IRequest from repoze.bfg.interfaces import IView - from repoze.bfg.view import NotFound + from repoze.bfg.exceptions import NotFound context = DummyContext() class IFoo(Interface): pass @@ -679,7 +679,7 @@ class TestViewDirective(unittest.TestCase): from zope.interface import Interface from repoze.bfg.interfaces import IRequest from repoze.bfg.interfaces import IView - from repoze.bfg.view import NotFound + from repoze.bfg.exceptions import NotFound context = DummyContext() class IFoo(Interface): pass @@ -735,7 +735,7 @@ class TestViewDirective(unittest.TestCase): from zope.interface import Interface from repoze.bfg.interfaces import IRequest from repoze.bfg.interfaces import IView - from repoze.bfg.view import NotFound + from repoze.bfg.exceptions import NotFound context = DummyContext() class IFoo(Interface): pass diff --git a/repoze/bfg/view.py b/repoze/bfg/view.py index 8d8d6baa0..91ccad57d 100644 --- a/repoze/bfg/view.py +++ b/repoze/bfg/view.py @@ -31,13 +31,17 @@ from repoze.bfg.interfaces import IRendererFactory from repoze.bfg.interfaces import IResponseFactory from repoze.bfg.interfaces import IView +from repoze.bfg.exceptions import NotFound +from repoze.bfg.exceptions import Forbidden from repoze.bfg.path import caller_package from repoze.bfg.renderers import renderer_from_name from repoze.bfg.resource import resource_spec -from repoze.bfg.security import Unauthorized from repoze.bfg.settings import get_settings from repoze.bfg.static import PackageURLParser +# b/c imports +from repoze.bfg.security import view_execution_permitted + try: all = all except NameError: # pragma: no cover @@ -53,6 +57,12 @@ deprecated('view_execution_permitted', "repoze.bfg.security import view_execution_permitted')", ) +deprecated('NotFound', + "('from repoze.bfg.view import NotFound' was " + "deprecated as of repoze.bfg 1.1; instead use 'from " + "repoze.bfg.exceptions import NotFound')", + ) + _marker = object() def render_view_to_response(context, request, name='', secure=True): @@ -64,7 +74,7 @@ def render_view_to_response(context, request, name='', secure=True): protected by a permission, the permission will be checked before calling the view function. If the permission check disallows view execution (based on the current security policy), a - ``repoze.bfg.security.Unauthorized`` exception will be raised; its + ``repoze.bfg.exceptions.Forbidden`` exception will be raised; its ``args`` attribute explains why the view access was disallowed. If ``secure`` is ``False``, no permission checking is done.""" provides = map(providedBy, (context, request)) @@ -78,7 +88,7 @@ def render_view_to_response(context, request, name='', secure=True): # secured; otherwise it won't. view = getattr(view, '__call_permissive__', view) - # if this view is secured, it will raise an Unauthorized + # if this view is secured, it will raise a Forbidden # appropriately if the executing user does not have the proper # permission return view(context, request) @@ -98,7 +108,7 @@ def render_view_to_iterable(context, request, name='', secure=True): a permission, the permission will be checked before calling the view function. If the permission check disallows view execution (based on the current security policy), a - ``repoze.bfg.security.Unauthorized`` exception will be raised; its + ``repoze.bfg.exceptions.Forbidden`` exception will be raised; its ``args`` attribute explains why the view access was disallowed. If ``secure`` is ``False``, no permission checking is done.""" response = render_view_to_response(context, request, name, secure) @@ -119,7 +129,7 @@ def render_view(context, request, name='', secure=True): permission, the permission will be checked before calling the view function. If the permission check disallows view execution (based on the current security policy), a - ``repoze.bfg.security.Unauthorized`` exception will be raised; its + ``repoze.bfg.exceptions.Forbidden`` exception will be raised; its ``args`` attribute explains why the view access was disallowed. If ``secure`` is ``False``, no permission checking is done.""" iterable = render_view_to_iterable(context, request, name, secure) @@ -386,9 +396,6 @@ def default_forbidden_view(context, request): def default_notfound_view(context, request): return default_view(context, request, '404 Not Found') -class NotFound(Exception): - pass - class MultiView(object): implements(IMultiView) @@ -664,7 +671,7 @@ def secure_view(view, permission): return view(context, request) msg = getattr(request, 'authdebug_message', 'Unauthorized: %s failed permission check' % view) - raise Unauthorized(msg) + raise Forbidden(msg) _secured_view.__call_permissive__ = view def _permitted(context, request): principals = authn_policy.effective_principals(request) @@ -711,4 +718,3 @@ def authdebug_view(view, permission): decorate_view(wrapped_view, view) return wrapped_view - |
