summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-05-24 23:12:59 +0000
committerChris McDonough <chrism@agendaless.com>2009-05-24 23:12:59 +0000
commit86ed4016ea6a681d4f579ace62cea032a679544d (patch)
tree697f67bbcb9e55e45db5d9aae52f0c3280e9b1e2
parentab5959d3d4e4603a61b3559096da30d2adfdcf4b (diff)
downloadpyramid-86ed4016ea6a681d4f579ace62cea032a679544d.tar.gz
pyramid-86ed4016ea6a681d4f579ace62cea032a679544d.tar.bz2
pyramid-86ed4016ea6a681d4f579ace62cea032a679544d.zip
Features
-------- - It is now possible to write a custom security policy that returns a customized ``Forbidden`` WSGI application when BFG cannot authorize an invocation of a view. To this end, ISecurityPolicy objects must now have a ``forbidden`` method. This method should return a WSGI application. The returned WSGI application should generate a response which is appropriate when access to a view resource was forbidden by the security policy (e.g. perhaps a login page). ``repoze.bfg`` is willing to operate with a custom security policy that does not have a ``forbidden`` method, but it will issue a warning; eventually security policies without a ``forbidden`` method will cease to work under ``repoze.bfg``. Note that the ``forbidden`` WSGI application returned by the security policy is not used if a developer has registered an IForbiddenAppFactory (see the "Hooks" narrative chapter); the explicitly registered IForbiddenAppFactory will be preferred over the (more general) security policy forbidden app factory. - All default security policies now have a ``forbidden`` callable attached to them. This particular callable returns a WSGI application which generates a ``401 Unauthorized`` response for backwards compatibility (had backwards compatibility not been an issue, this callable would have returned a WSGI app that generated a ``403 Forbidden`` response). Backwards Incompatibilities --------------------------- - Custom NotFound and Forbidden (nee' Unauthorized) WSGI applications (registered a a utility for INotFoundAppFactory and IUnauthorizedAppFactory) could rely on an environment key named ``message`` describing the circumstance of the response. This key has been renamed to ``repoze.bfg.message`` (as per the WSGI spec, which requires environment extensions to contain dots). Deprecations ------------ - The ``repoze.bfg.interfaces.IUnauthorizedAppFactory`` interface has been renamed to ``repoze.bfg.interfaces.IForbiddenAppFactory``.
-rw-r--r--CHANGES.txt48
-rw-r--r--docs/narr/hooks.rst54
-rw-r--r--repoze/bfg/interfaces.py19
-rw-r--r--repoze/bfg/router.py46
-rw-r--r--repoze/bfg/security.py9
-rw-r--r--repoze/bfg/tests/test_router.py186
-rw-r--r--repoze/bfg/tests/test_security.py23
-rw-r--r--repoze/bfg/tests/test_wsgi.py4
-rw-r--r--repoze/bfg/wsgi.py2
9 files changed, 250 insertions, 141 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 40a810305..3650b674f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,51 @@
+Next release
+============
+
+Features
+--------
+
+- It is now possible to write a custom security policy that returns a
+ customized ``Forbidden`` WSGI application when BFG cannot authorize
+ an invocation of a view. To this end, ISecurityPolicy objects must
+ now have a ``forbidden`` method. This method should return a WSGI
+ application. The returned WSGI application should generate a
+ response which is appropriate when access to a view resource was
+ forbidden by the security policy (e.g. perhaps a login page).
+ ``repoze.bfg`` is willing to operate with a custom security policy
+ that does not have a ``forbidden`` method, but it will issue a
+ warning; eventually security policies without a ``forbidden`` method
+ will cease to work under ``repoze.bfg``.
+
+ Note that the ``forbidden`` WSGI application returned by the
+ security policy is not used if a developer has registered an
+ IForbiddenAppFactory (see the "Hooks" narrative chapter); the
+ explicitly registered IForbiddenAppFactory will be preferred over
+ the (more general) security policy forbidden app factory.
+
+- All default security policies now have a ``forbidden`` callable
+ attached to them. This particular callable returns a WSGI
+ application which generates a ``401 Unauthorized`` response for
+ backwards compatibility (had backwards compatibility not been an
+ issue, this callable would have returned a WSGI app that generated a
+ ``403 Forbidden`` response).
+
+Backwards Incompatibilities
+---------------------------
+
+- Custom NotFound and Forbidden (nee' Unauthorized) WSGI applications
+ (registered a a utility for INotFoundAppFactory and
+ IUnauthorizedAppFactory) could rely on an environment key named
+ ``message`` describing the circumstance of the response. This key
+ has been renamed to ``repoze.bfg.message`` (as per the WSGI spec,
+ which requires environment extensions to contain dots).
+
+Deprecations
+------------
+
+- The ``repoze.bfg.interfaces.IUnauthorizedAppFactory`` interface has
+ been renamed to ``repoze.bfg.interfaces.IForbiddenAppFactory``.
+
+
0.8.1 (2009-05-21)
==================
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index 21906e466..5eab5e41e 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -88,7 +88,7 @@ an object that implements any particular interface; it simply needs
have a ``status`` attribute, a ``headerlist`` attribute, and and
``app_iter`` attribute.
-Changing the NotFound application
+Changing the NotFound Application
---------------------------------
When :mod:`repoze.bfg` can't map a URL to code, it creates and invokes
@@ -119,54 +119,54 @@ sample code that implements a minimal NotFound application factory:
.. note:: When a NotFound application factory is invoked, it is passed
the WSGI environ and the WSGI ``start_response`` handler by
:mod:`repoze.bfg`. Within the WSGI environ will be a key named
- ``message`` that has a value explaining why the not found error was
- raised. This error will be different when the ``debug_notfound``
- environment setting is true than it is when it is false.
+ ``repoze.bfg.message`` that has a value explaining why the not
+ found error was raised. This error will be different when the
+ ``debug_notfound`` environment setting is true than it is when it
+ is false.
-Changing the Unauthorized application
--------------------------------------
+Changing the Forbidden Application
+----------------------------------
When :mod:`repoze.bfg` can't authorize execution of a view based on
-the security policy in use, it creates and invokes an Unauthorized
-WSGI application. The application it invokes can be customized by
-placing something like the following ZCML in your ``configure.zcml``
-file.
+the security policy in use, it creates and invokes a Forbidden WSGI
+application. The application it invokes can be customized by placing
+something like the following ZCML in your ``configure.zcml`` file.
.. code-block:: xml
:linenos:
- <utility provides="repoze.bfg.interfaces.IUnauthorizedAppFactory"
- component="helloworld.factories.unauthorized_app_factory"/>
+ <utility provides="repoze.bfg.interfaces.IForbiddenAppFactory"
+ component="helloworld.factories.forbidden_app_factory"/>
-Replace ``helloworld.factories.unauthorized_app_factory`` with the
-Python dotted name to the request factory you want to use. Here's
+Replace ``helloworld.factories.forbidden_app_factory`` with the Python
+dotted name to the WSGI application factory you want to use. Here's
some sample code that implements a minimal Unauthorized application
factory:
.. code-block:: python
- from webob.exc import HTTPUnauthorized
+ from webob.exc import HTTPForbidden
- class MyUnauthorized(HTTPUnauthorized):
+ class MyForbidden(HTTPForbidden):
pass
- def notfound_app_factory():
- return MyUnauthorized
+ def forbidden_app_factory():
+ return MyForbidden
-.. note:: When an Unauthorized application factory is invoked, it is
+.. note:: When an Forbidden application factory is invoked, it is
passed the WSGI environ and the WSGI ``start_response`` handler by
:mod:`repoze.bfg`. Within the WSGI environ will be a key named
- ``message`` that has a value explaining why the action was not
- authorized. This error will be different when the
+ ``repoze.bfg.message`` that has a value explaining why the action
+ was forbidden. This error will be different when the
``debug_authorization`` environment setting is true than it is when
it is false.
-.. note:: You can influence the status code of Unauthorized responses
- by using an alterate unauthorized application factory. For
- example, you may return an unauthorized application with a ``403
- Forbidden`` status code, rather than use the default unauthorized
- application factory, which sends a response with a ``401
- Unauthorized`` status code.
+.. warning:: the default forbidden application factory sends a
+ response with a ``401 Unauthorized`` status code for backwards
+ compatibility reasons. You can influence the status code of
+ Forbidden responses by using an alterate forbidden application
+ factory. For example, it would make sense to return an forbidden
+ application with a ``403 Forbidden`` status code.
Changing the Default Routes Context Factory
-------------------------------------------
diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py
index cecc3a397..034e0ac1c 100644
--- a/repoze/bfg/interfaces.py
+++ b/repoze/bfg/interfaces.py
@@ -131,6 +131,17 @@ class ISecurityPolicy(Interface):
implementation, in which case, it should raise a
``NotImplementedError`` exception."""
+ def forbidden():
+ """ This method should return a WSGI application (a callable
+ accepting ``environ`` and ``start_response``). This WSGI
+ application will be called by ``repoze.bfg`` when view
+ invocation is denied due to a security policy deny. The WSGI
+ application should return a response appropriate when access
+ to a view resource was forbidden by the security policy. Note
+ that the ``repoze.bfg.message`` key in the environ passed to
+ the WSGI app will contain the 'raw' reason that view
+ invocation was denied by repoze.bfg."""
+
class IViewPermission(Interface):
def __call__(security_policy):
""" Return True if the permission allows, return False if it denies. """
@@ -197,7 +208,7 @@ class INotFoundAppFactory(Interface):
a``message`` key in the WSGI environ provides information
pertaining to the reason for the notfound."""
-class IUnauthorizedAppFactory(Interface):
+class IForbiddenAppFactory(Interface):
""" A utility which returns an Unauthorized WSGI application
factory"""
def __call__():
@@ -206,6 +217,12 @@ class IUnauthorizedAppFactory(Interface):
``message`` key in the WSGI environ provides information
pertaining to the reason for the unauthorized."""
+IUnauthorizedAppFactory = IForbiddenAppFactory
+deprecated('IUnauthorizedAppFactory',
+ '(repoze.bfg.interfaces.IUnauthorizedAppFactory should now be '
+ 'imported as repoze.bfg.interfaces.IForbiddenAppFactory)',
+ )
+
class IContextURL(Interface):
""" An adapter which deals with URLs related to a context.
"""
diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py
index 81bc6e4ef..885a08184 100644
--- a/repoze/bfg/router.py
+++ b/repoze/bfg/router.py
@@ -17,7 +17,7 @@ from repoze.bfg.interfaces import IRouter
from repoze.bfg.interfaces import IRoutesMapper
from repoze.bfg.interfaces import ISecurityPolicy
from repoze.bfg.interfaces import ISettings
-from repoze.bfg.interfaces import IUnauthorizedAppFactory
+from repoze.bfg.interfaces import IForbiddenAppFactory
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IViewPermission
@@ -50,22 +50,40 @@ class Router(object):
def __init__(self, registry):
self.registry = registry
+ self.logger = registry.queryUtility(ILogger, 'repoze.bfg.debug')
self.request_factory = registry.queryUtility(IRequestFactory)
- self.security_policy = registry.queryUtility(ISecurityPolicy)
- self.notfound_app_factory = registry.queryUtility(
- INotFoundAppFactory,
- default=NotFound)
- self.unauth_app_factory = registry.queryUtility(
- IUnauthorizedAppFactory,
- default=Unauthorized)
+ security_policy = registry.queryUtility(ISecurityPolicy)
+
+ self.forbidden_app_factory = registry.queryUtility(IForbiddenAppFactory)
+ if security_policy is not None:
+ if hasattr(security_policy, 'forbidden'):
+ security_policy_forbidden = security_policy.forbidden
+ else:
+ security_policy_forbidden = Unauthorized
+ warning = ('You are running with a security policy (%s) which '
+ 'does not have a "forbidden" method; in BFG 0.8.2+ '
+ 'the ISecurityPolicy interface in the '
+ 'repoze.bfg.interfaces module defines this method '
+ 'as required; your application will not work under '
+ 'a future release of BFG if you continue using a '
+ 'security policy without a "forbidden" method.' %
+ security_policy)
+ self.logger and self.logger.warn(warning)
+ # allow a specifically-registered IForbiddenAppFactory to
+ # override the security policy's forbidden
+ self.forbidden_app_factory = (self.forbidden_app_factory or
+ security_policy_forbidden)
+
+ self.security_policy = security_policy
+ self.notfound_app_factory = registry.queryUtility(INotFoundAppFactory,
+ default=NotFound)
settings = registry.queryUtility(ISettings)
if settings is not None:
self.debug_authorization = settings.debug_authorization
self.debug_notfound = settings.debug_notfound
- self.logger = registry.queryUtility(ILogger, 'repoze.bfg.debug')
self.root_factory = registry.getUtility(IRootFactory)
self.root_policy = self.root_factory # b/w compat
self.traverser_warned = {}
@@ -144,14 +162,16 @@ class Router(object):
'context %r): %s' % (
request.url, view_name, context, permitted)
)
+
if not permitted:
if debug_authorization:
msg = str(permitted)
else:
msg = 'Unauthorized: failed security policy check'
- environ['message'] = msg
- unauth_app = self.unauth_app_factory()
- return unauth_app(environ, start_response)
+
+ environ['repoze.bfg.message'] = msg
+
+ return self.forbidden_app_factory()(environ, start_response)
response = registry.queryMultiAdapter(
(context, request), IView, name=view_name)
@@ -168,7 +188,7 @@ class Router(object):
logger and logger.debug(msg)
else:
msg = request.url
- environ['message'] = msg
+ environ['repoze.bfg.message'] = msg
notfound_app = self.notfound_app_factory()
return notfound_app(environ, start_response)
diff --git a/repoze/bfg/security.py b/repoze/bfg/security.py
index 90916bac2..bd1edaf6d 100644
--- a/repoze/bfg/security.py
+++ b/repoze/bfg/security.py
@@ -8,6 +8,8 @@ from repoze.bfg.interfaces import ISecurityPolicy
from repoze.bfg.interfaces import IViewPermission
from repoze.bfg.interfaces import IViewPermissionFactory
+from repoze.bfg.wsgi import Unauthorized as UnauthorizedApp
+
Everyone = 'system.Everyone'
Authenticated = 'system.Authenticated'
Allow = 'Allow'
@@ -145,6 +147,8 @@ class ACLSecurityPolicy(object):
return []
+ forbidden = UnauthorizedApp
+
class InheritingACLSecurityPolicy(object):
""" A security policy which uses ACLs in the following ways:
@@ -268,6 +272,8 @@ class InheritingACLSecurityPolicy(object):
return allowed
+ forbidden = UnauthorizedApp
+
def get_remoteuser(request):
user_id = request.environ.get('REMOTE_USER')
if user_id:
@@ -595,6 +601,3 @@ class ViewPermissionFactory(object):
class Unauthorized(Exception):
pass
-
-
-
diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py
index db47f832e..d7c55ae78 100644
--- a/repoze/bfg/tests/test_router.py
+++ b/repoze/bfg/tests/test_router.py
@@ -78,9 +78,11 @@ class RouterTests(unittest.TestCase):
from repoze.bfg.interfaces import IViewPermission
self.registry.registerAdapter(permission, for_, IViewPermission, name)
- def _registerSecurityPolicy(self, secpol):
+ def _registerSecurityPolicy(self):
+ secpol = DummySecurityPolicy()
from repoze.bfg.interfaces import ISecurityPolicy
self.registry.registerUtility(secpol, ISecurityPolicy)
+ return secpol
def _registerEventListener(self, iface):
L = []
@@ -89,9 +91,11 @@ class RouterTests(unittest.TestCase):
self.registry.registerHandler(listener, (iface,))
return L
- def _registerRootFactory(self, root_factory):
+ def _registerRootFactory(self, val):
+ rootfactory = make_rootfactory(val)
from repoze.bfg.interfaces import IRootFactory
- self.registry.registerUtility(root_factory, IRootFactory)
+ self.registry.registerUtility(rootfactory, IRootFactory)
+ return rootfactory
def _getTargetClass(self):
from repoze.bfg.router import Router
@@ -113,21 +117,67 @@ class RouterTests(unittest.TestCase):
return environ
def test_root_policy(self):
- rootfactory = make_rootfactory(None)
environ = self._makeEnviron()
context = DummyContext()
self._registerTraverserFactory(context)
- self._registerRootFactory(rootfactory)
+ rootfactory = self._registerRootFactory(None)
router = self._makeOne()
self.assertEqual(router.root_policy, rootfactory)
+ def test_secpol_no_forbidden(self):
+ environ = self._makeEnviron()
+ context = DummyContext()
+ self._registerTraverserFactory(context)
+ rootfactory = self._registerRootFactory(None)
+ logger = self._registerLogger()
+ secpol = self._registerSecurityPolicy()
+ del secpol.forbidden
+ router = self._makeOne()
+ self.assertEqual(len(logger.messages), 1)
+ self.failUnless('which does not have a "forbidden" method'
+ in logger.messages[0])
+
+ def test_inotfound_appfactory_override(self):
+ from repoze.bfg.interfaces import INotFoundAppFactory
+ def app():
+ """ """
+ self.registry.registerUtility(app, INotFoundAppFactory)
+ self._registerRootFactory(None)
+ router = self._makeOne()
+ self.assertEqual(router.notfound_app_factory, app)
+
+ def test_iforbidden_appfactory_override_withsecpol(self):
+ from repoze.bfg.interfaces import IForbiddenAppFactory
+ def app():
+ """ """
+ self.registry.registerUtility(app, IForbiddenAppFactory)
+ self._registerSecurityPolicy()
+ self._registerRootFactory(None)
+ router = self._makeOne()
+ self.assertEqual(router.forbidden_app_factory, app)
+
+ def test_iforbidden_appfactory_override_nosecpol(self):
+ from repoze.bfg.interfaces import IForbiddenAppFactory
+ def app():
+ """ """
+ self.registry.registerUtility(app, IForbiddenAppFactory)
+ self._registerRootFactory(None)
+ router = self._makeOne()
+ self.assertEqual(router.forbidden_app_factory, app)
+
+ def test_iforbidden_appfactory_nooverride(self):
+ secpol = self._registerSecurityPolicy()
+ context = DummyContext()
+ self._registerRootFactory(None)
+ router = self._makeOne()
+ self.assertEqual(router.forbidden_app_factory, secpol.forbidden)
+
def test_call_no_view_registered_no_isettings(self):
- rootfactory = make_rootfactory(None)
environ = self._makeEnviron()
context = DummyContext()
self._registerTraverserFactory(context)
logger = self._registerLogger()
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
@@ -148,8 +198,7 @@ class RouterTests(unittest.TestCase):
self._registerTraverserFactory(context)
environ = self._makeEnviron()
start_response = DummyStartResponse()
- rootfactory = make_rootfactory(NotFound())
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(NotFound())
router = self._makeOne()
result = router(environ, start_response)
status = start_response.status
@@ -157,13 +206,12 @@ class RouterTests(unittest.TestCase):
self.failUnless('http://localhost:8080' in result[0], result)
def test_call_no_view_registered_debug_notfound_false(self):
- rootfactory = make_rootfactory(None)
environ = self._makeEnviron()
context = DummyContext()
self._registerTraverserFactory(context)
logger = self._registerLogger()
self._registerSettings(debug_notfound=False)
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
@@ -176,13 +224,12 @@ class RouterTests(unittest.TestCase):
self.assertEqual(len(logger.messages), 0)
def test_call_no_view_registered_debug_notfound_true(self):
- rootfactory = make_rootfactory(None)
environ = self._makeEnviron()
context = DummyContext()
self._registerTraverserFactory(context)
self._registerSettings(debug_notfound=True)
logger = self._registerLogger()
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
@@ -205,19 +252,17 @@ class RouterTests(unittest.TestCase):
self.failUnless("subpath: []" in message)
def test_call_view_returns_nonresponse(self):
- rootfactory = make_rootfactory(None)
context = DummyContext()
self._registerTraverserFactory(context)
environ = self._makeEnviron()
view = make_view('abc')
self._registerView(view, '', None, None)
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
self.assertRaises(ValueError, router, environ, start_response)
def test_call_view_registered_nonspecific_default_path(self):
- rootfactory = make_rootfactory(None)
context = DummyContext()
self._registerTraverserFactory(context)
response = DummyResponse()
@@ -225,7 +270,7 @@ class RouterTests(unittest.TestCase):
view = make_view(response)
environ = self._makeEnviron()
self._registerView(view, '', None, None)
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
@@ -238,7 +283,6 @@ class RouterTests(unittest.TestCase):
self.assertEqual(environ['webob.adhoc_attrs']['root'], None)
def test_call_deprecation_warning(self):
- rootfactory = make_rootfactory(None)
context = DummyContext()
self._registerTraverserFactory(context, _deprecation_warning='abc')
response = DummyResponse()
@@ -246,7 +290,7 @@ class RouterTests(unittest.TestCase):
view = make_view(response)
environ = self._makeEnviron()
self._registerView(view, '', None, None)
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
logger = self._registerLogger()
router.logger = logger
@@ -256,7 +300,6 @@ class RouterTests(unittest.TestCase):
self.assertEqual(logger.messages[0], 'abc')
def test_call_view_registered_nonspecific_nondefault_path_and_subpath(self):
- rootfactory = make_rootfactory(None)
context = DummyContext()
self._registerTraverserFactory(context, view_name='foo',
subpath=['bar'],
@@ -266,7 +309,7 @@ class RouterTests(unittest.TestCase):
view = make_view(response)
environ = self._makeEnviron()
self._registerView(view, 'foo', None, None)
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
@@ -279,7 +322,6 @@ class RouterTests(unittest.TestCase):
self.assertEqual(environ['webob.adhoc_attrs']['root'], None)
def test_call_view_registered_specific_success(self):
- rootfactory = make_rootfactory(None)
from zope.interface import Interface
from zope.interface import directlyProvides
class IContext(Interface):
@@ -293,7 +335,7 @@ class RouterTests(unittest.TestCase):
view = make_view(response)
environ = self._makeEnviron()
self._registerView(view, '', IContext, IRequest)
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
@@ -306,7 +348,6 @@ class RouterTests(unittest.TestCase):
self.assertEqual(environ['webob.adhoc_attrs']['root'], None)
def test_call_view_registered_specific_fail(self):
- rootfactory = make_rootfactory(None)
from zope.interface import Interface
from zope.interface import directlyProvides
class IContext(Interface):
@@ -321,7 +362,7 @@ class RouterTests(unittest.TestCase):
view = make_view(response)
environ = self._makeEnviron()
self._registerView(view, '', IContext, IRequest)
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
@@ -329,7 +370,6 @@ class RouterTests(unittest.TestCase):
self.failUnless('404' in result[0])
def test_call_view_registered_security_policy_permission_none(self):
- rootfactory = make_rootfactory(None)
from zope.interface import Interface
from zope.interface import directlyProvides
class IContext(Interface):
@@ -342,16 +382,14 @@ class RouterTests(unittest.TestCase):
view = make_view(response)
environ = self._makeEnviron()
self._registerView(view, '', IContext, IRequest)
- secpol = DummySecurityPolicy()
- self._registerSecurityPolicy(secpol)
- self._registerRootFactory(rootfactory)
+ self._registerSecurityPolicy()
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
self.assertEqual(start_response.status, '200 OK')
def test_call_view_registered_security_policy_permission_succeeds(self):
- rootfactory = make_rootfactory(None)
from zope.interface import Interface
from zope.interface import directlyProvides
class IContext(Interface):
@@ -362,13 +400,12 @@ class RouterTests(unittest.TestCase):
self._registerTraverserFactory(context, subpath=[''])
response = DummyResponse()
view = make_view(response)
- secpol = DummySecurityPolicy()
permissionfactory = make_permission_factory(True)
environ = self._makeEnviron()
self._registerView(view, '', IContext, IRequest)
- self._registerSecurityPolicy(secpol)
+ secpol = self._registerSecurityPolicy()
self._registerPermission(permissionfactory, '', IContext, IRequest)
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
@@ -376,7 +413,6 @@ class RouterTests(unittest.TestCase):
self.assertEqual(permissionfactory.checked_with, secpol)
def test_call_view_permission_fails_nosettings(self):
- rootfactory = make_rootfactory(None)
from zope.interface import Interface
from zope.interface import directlyProvides
class IContext(Interface):
@@ -387,26 +423,24 @@ class RouterTests(unittest.TestCase):
self._registerTraverserFactory(context, subpath=[''])
response = DummyResponse()
view = make_view(response)
- secpol = DummySecurityPolicy()
from repoze.bfg.security import ACLDenied
permissionfactory = make_permission_factory(
ACLDenied('ace', 'acl', 'permission', ['principals'], context)
)
environ = self._makeEnviron()
self._registerView(view, '', IContext, IRequest)
- self._registerSecurityPolicy(secpol)
+ secpol = self._registerSecurityPolicy()
self._registerPermission(permissionfactory, '', IContext, IRequest)
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
self.assertEqual(start_response.status, '401 Unauthorized')
- message = result[0]
- self.failUnless('failed security policy check' in message)
+ message = environ['repoze.bfg.message']
+ self.assertEqual(message, 'Unauthorized: failed security policy check')
self.assertEqual(permissionfactory.checked_with, secpol)
def test_call_view_permission_fails_no_debug_auth(self):
- rootfactory = make_rootfactory(None)
from zope.interface import Interface
from zope.interface import directlyProvides
class IContext(Interface):
@@ -417,27 +451,25 @@ class RouterTests(unittest.TestCase):
self._registerTraverserFactory(context, subpath=[''])
response = DummyResponse()
view = make_view(response)
- secpol = DummySecurityPolicy()
from repoze.bfg.security import ACLDenied
permissionfactory = make_permission_factory(
ACLDenied('ace', 'acl', 'permission', ['principals'], context)
)
environ = self._makeEnviron()
self._registerView(view, '', IContext, IRequest)
- self._registerSecurityPolicy(secpol)
+ secpol = self._registerSecurityPolicy()
self._registerPermission(permissionfactory, '', IContext, IRequest)
self._registerSettings(debug_authorization=False)
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
self.assertEqual(start_response.status, '401 Unauthorized')
- message = result[0]
+ message = environ['repoze.bfg.message']
self.failUnless('failed security policy check' in message)
self.assertEqual(permissionfactory.checked_with, secpol)
def test_call_view_permission_fails_with_debug_auth(self):
- rootfactory = make_rootfactory(None)
from zope.interface import Interface
from zope.interface import directlyProvides
class IContext(Interface):
@@ -448,23 +480,22 @@ class RouterTests(unittest.TestCase):
self._registerTraverserFactory(context, subpath=[''])
response = DummyResponse()
view = make_view(response)
- secpol = DummySecurityPolicy()
from repoze.bfg.security import ACLDenied
permissionfactory = make_permission_factory(
ACLDenied('ace', 'acl', 'permission', ['principals'], context)
)
environ = self._makeEnviron()
self._registerView(view, '', IContext, IRequest)
- self._registerSecurityPolicy(secpol)
+ secpol = self._registerSecurityPolicy()
self._registerPermission(permissionfactory, '', IContext, IRequest)
self._registerSettings(debug_authorization=True)
logger = self._registerLogger()
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
self.assertEqual(start_response.status, '401 Unauthorized')
- message = result[0]
+ message = environ['repoze.bfg.message']
self.failUnless(
"ACLDenied permission 'permission' via ACE 'ace' in ACL 'acl' "
"on context" in message)
@@ -482,7 +513,6 @@ class RouterTests(unittest.TestCase):
"for principals ['principals']" in logged)
def test_call_eventsends(self):
- rootfactory = make_rootfactory(None)
context = DummyContext()
self._registerTraverserFactory(context)
response = DummyResponse()
@@ -494,7 +524,7 @@ class RouterTests(unittest.TestCase):
from repoze.bfg.interfaces import INewResponse
request_events = self._registerEventListener(INewRequest)
response_events = self._registerEventListener(INewResponse)
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
@@ -508,7 +538,6 @@ class RouterTests(unittest.TestCase):
from repoze.bfg.interfaces import IPOSTRequest
from repoze.bfg.interfaces import IPUTRequest
from repoze.bfg.interfaces import IRequest
- rootfactory = make_rootfactory(None)
context = DummyContext()
self._registerTraverserFactory(context)
response = DummyResponse()
@@ -516,7 +545,7 @@ class RouterTests(unittest.TestCase):
view = make_view(response)
environ = self._makeEnviron(REQUEST_METHOD='POST')
self._registerView(view, '', None, None)
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
request_events = self._registerEventListener(INewRequest)
@@ -531,7 +560,6 @@ class RouterTests(unittest.TestCase):
from repoze.bfg.interfaces import IPUTRequest
from repoze.bfg.interfaces import IPOSTRequest
from repoze.bfg.interfaces import IRequest
- rootfactory = make_rootfactory(None)
context = DummyContext()
self._registerTraverserFactory(context)
response = DummyResponse()
@@ -539,7 +567,7 @@ class RouterTests(unittest.TestCase):
view = make_view(response)
environ = self._makeEnviron(REQUEST_METHOD='PUT')
self._registerView(view, '', None, None)
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
request_events = self._registerEventListener(INewRequest)
@@ -552,7 +580,6 @@ class RouterTests(unittest.TestCase):
def test_call_unknown_method(self):
from repoze.bfg.interfaces import INewRequest
from repoze.bfg.interfaces import IRequest
- rootfactory = make_rootfactory(None)
context = DummyContext()
self._registerTraverserFactory(context)
response = DummyResponse()
@@ -560,7 +587,7 @@ class RouterTests(unittest.TestCase):
view = make_view(response)
environ = self._makeEnviron(REQUEST_METHOD='UNKNOWN')
self._registerView(view, '', None, None)
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
request_events = self._registerEventListener(INewRequest)
@@ -573,7 +600,6 @@ class RouterTests(unittest.TestCase):
from repoze.bfg.interfaces import IRequestFactory
from repoze.bfg.testing import DummyRequest
self.registry.registerUtility(DummyRequest, IRequestFactory)
- rootfactory = make_rootfactory(None)
context = DummyContext()
self._registerTraverserFactory(context)
response = DummyResponse()
@@ -581,7 +607,7 @@ class RouterTests(unittest.TestCase):
view = make_view(response)
environ = self._makeEnviron()
self._registerView(view, '', None, None)
- self._registerRootFactory(rootfactory)
+ self._registerRootFactory(None)
router = self._makeOne()
start_response = DummyStartResponse()
request_events = self._registerEventListener(INewRequest)
@@ -593,40 +619,6 @@ class RouterTests(unittest.TestCase):
self.assertEqual(request.view_name, '')
self.assertEqual(request.subpath, [])
- def test_call_inotfound_appfactory_override(self):
- from repoze.bfg.interfaces import INotFoundAppFactory
- def app():
- """ """
- self.registry.registerUtility(app, INotFoundAppFactory)
- rootfactory = make_rootfactory(None)
- context = DummyContext()
- self._registerTraverserFactory(context)
- response = DummyResponse()
- response.app_iter = ['Hello world']
- view = make_view(response)
- environ = self._makeEnviron()
- self._registerView(view, '', None, None)
- self._registerRootFactory(rootfactory)
- router = self._makeOne()
- self.assertEqual(router.notfound_app_factory, app)
-
- def test_call_iunauth_appfactory_override(self):
- from repoze.bfg.interfaces import IUnauthorizedAppFactory
- def app():
- """ """
- self.registry.registerUtility(app, IUnauthorizedAppFactory)
- rootfactory = make_rootfactory(None)
- context = DummyContext()
- self._registerTraverserFactory(context)
- response = DummyResponse()
- response.app_iter = ['Hello world']
- view = make_view(response)
- environ = self._makeEnviron()
- self._registerView(view, '', None, None)
- self._registerRootFactory(rootfactory)
- router = self._makeOne()
- self.assertEqual(router.unauth_app_factory, app)
-
class MakeAppTests(unittest.TestCase):
def setUp(self):
cleanUp()
@@ -774,5 +766,11 @@ class DummyResponse:
app_iter = ()
class DummySecurityPolicy:
- pass
+ def __init__(self):
+ def wsgiapp(environ, start_response):
+ self.environ = environ
+ self.start_response = start_response
+ start_response('401 Unauthorized', [])
+ return 'Unauthorized'
+ self.forbidden = lambda *x: wsgiapp
diff --git a/repoze/bfg/tests/test_security.py b/repoze/bfg/tests/test_security.py
index 03a466e7c..b596a1547 100644
--- a/repoze/bfg/tests/test_security.py
+++ b/repoze/bfg/tests/test_security.py
@@ -243,6 +243,18 @@ class TestACLSecurityPolicy(unittest.TestCase):
result = policy.principals_allowed_by_permission(None, 'read')
self.assertEqual(result, [])
+ def test_forbidden(self):
+ policy = self._makeOne(lambda *arg: None)
+ forbidden_app = policy.forbidden()
+ environ = {}
+ result = []
+ def start_response(status, headers):
+ result.append((status, headers))
+ response = forbidden_app(environ, start_response)
+ self.assertEqual(result[0][0], '401 Unauthorized')
+ self.failUnless(len(result[0][1]), 2) # headers
+
+
class TestInheritingACLSecurityPolicy(unittest.TestCase):
def setUp(self):
cleanUp()
@@ -430,6 +442,17 @@ class TestInheritingACLSecurityPolicy(unittest.TestCase):
result = policy.authenticated_userid(request)
self.assertEqual(result, None)
+ def test_forbidden(self):
+ policy = self._makeOne(lambda *arg: None)
+ forbidden_app = policy.forbidden()
+ environ = {}
+ result = []
+ def start_response(status, headers):
+ result.append((status, headers))
+ response = forbidden_app(environ, start_response)
+ self.assertEqual(result[0][0], '401 Unauthorized')
+ self.failUnless(len(result[0][1]), 2) # headers
+
class TestAllPermissionsList(unittest.TestCase):
def setUp(self):
cleanUp()
diff --git a/repoze/bfg/tests/test_wsgi.py b/repoze/bfg/tests/test_wsgi.py
index b9568eb82..893364635 100644
--- a/repoze/bfg/tests/test_wsgi.py
+++ b/repoze/bfg/tests/test_wsgi.py
@@ -131,7 +131,7 @@ class TestNotFound(unittest.TestCase):
('Content-Type', 'text/html')])
def test_with_message(self):
- environ = {'message':'<hi!>'}
+ environ = {'repoze.bfg.message':'<hi!>'}
L = []
def start_response(status, headers):
L.append((status, headers))
@@ -166,7 +166,7 @@ class TestUnauthorized(unittest.TestCase):
('Content-Type', 'text/html')])
def test_with_message(self):
- environ = {'message':'<hi!>'}
+ environ = {'repoze.bfg.message':'<hi!>'}
L = []
def start_response(status, headers):
L.append((status, headers))
diff --git a/repoze/bfg/wsgi.py b/repoze/bfg/wsgi.py
index abe7ebead..027345673 100644
--- a/repoze/bfg/wsgi.py
+++ b/repoze/bfg/wsgi.py
@@ -105,7 +105,7 @@ def wsgiapp2(wrapped):
class HTTPException(object):
def __call__(self, environ, start_response, exc_info=False):
try:
- msg = escape(environ['message'])
+ msg = escape(environ['repoze.bfg.message'])
except KeyError:
msg = ''
html = """<body>