summaryrefslogtreecommitdiff
path: root/repoze/bfg/router.py
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-05-27 04:52:51 +0000
committerChris McDonough <chrism@agendaless.com>2009-05-27 04:52:51 +0000
commita1a9fb7128c935848b17c0ce6586991098a17f07 (patch)
tree5160f28be92202033c693caa335f8b9cda3c6379 /repoze/bfg/router.py
parent08ead74d05e25f58c83712f6f8651484ddc983d0 (diff)
downloadpyramid-a1a9fb7128c935848b17c0ce6586991098a17f07.tar.gz
pyramid-a1a9fb7128c935848b17c0ce6586991098a17f07.tar.bz2
pyramid-a1a9fb7128c935848b17c0ce6586991098a17f07.zip
Merge authchanges branch to trunk.
Diffstat (limited to 'repoze/bfg/router.py')
-rw-r--r--repoze/bfg/router.py231
1 files changed, 184 insertions, 47 deletions
diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py
index 81bc6e4ef..1b535c442 100644
--- a/repoze/bfg/router.py
+++ b/repoze/bfg/router.py
@@ -1,42 +1,55 @@
+from cgi import escape
import sys
from webob import Request as WebObRequest
+from webob import Response
from zope.component.event import dispatch
+from zope.component import queryUtility
from zope.interface import implements
+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.interfaces import ILogger
+from repoze.bfg.interfaces import ISecurityPolicy
from repoze.bfg.interfaces import INotFoundAppFactory
from repoze.bfg.interfaces import IRequestFactory
+from repoze.bfg.interfaces import IResponseFactory
from repoze.bfg.interfaces import IRootFactory
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 IForbiddenResponseFactory
from repoze.bfg.interfaces import IUnauthorizedAppFactory
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IViewPermission
+from repoze.bfg.interfaces import IAuthorizationPolicy
+from repoze.bfg.interfaces import IAuthenticationPolicy
from repoze.bfg.log import make_stream_logger
from repoze.bfg.registry import Registry
-from repoze.bfg.registry import registry_manager
from repoze.bfg.registry import populateRegistry
from repoze.bfg.request import HTTP_METHOD_FACTORIES
from repoze.bfg.request import Request
+from repoze.bfg.secpols import registerBBBAuthn
+
+from repoze.bfg.security import Allowed
+
from repoze.bfg.settings import Settings
-from repoze.bfg.urldispatch import RoutesRootFactory
+from repoze.bfg.threadlocal import manager
from repoze.bfg.traversal import _traverse
-from repoze.bfg.view import _view_execution_permitted
-from repoze.bfg.wsgi import Unauthorized
+
+from repoze.bfg.urldispatch import RoutesRootFactory
+
from repoze.bfg.wsgi import NotFound
_marker = object()
@@ -47,25 +60,51 @@ class Router(object):
debug_authorization = False
debug_notfound = False
+ threadlocal_manager = manager
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)
+ unauthorized_app_factory = registry.queryUtility(
+ IUnauthorizedAppFactory)
+
+ forbidden = None
+
+ if unauthorized_app_factory is not None:
+ warning = (
+ 'Instead of registering a utility against the '
+ 'repoze.bfg.interfaces.IUnauthorizedAppFactory interface '
+ 'to return a custom forbidden response, you should now '
+ 'register a "repoze.interfaces.IForbiddenResponseFactory". '
+ 'The IUnauthorizedAppFactory interface was deprecated in '
+ 'repoze.bfg 0.9 and will be removed in a subsequent version '
+ 'of repoze.bfg. See the "Hooks" chapter of the repoze.bfg '
+ 'documentation for more information about '
+ 'IForbiddenResponseFactory.')
+ self.logger and self.logger.warn(warning)
+ def forbidden(context, request):
+ app = unauthorized_app_factory()
+ response = request.get_response(app)
+ return response
+
+ forbidden = registry.queryUtility(IForbiddenResponseFactory,
+ default=forbidden)
+
+ self.forbidden_resp_factory = forbidden or default_forbidden_view
+
+ 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.secured = not not registry.queryUtility(IAuthenticationPolicy)
- 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 = {}
@@ -78,7 +117,11 @@ class Router(object):
iterable.
"""
registry = self.registry
- registry_manager.push(registry)
+ threadlocals = {'registry':registry, 'request':None}
+ self.threadlocal_manager.push(threadlocals)
+
+ logger = self.logger
+ request = None
try:
if self.request_factory is None:
@@ -92,6 +135,7 @@ class Router(object):
request_factory = self.request_factory
request = request_factory(environ)
+ threadlocals['request'] = request
registry.has_listeners and registry.notify(NewRequest(request))
root = self.root_factory(environ)
@@ -121,37 +165,48 @@ class Router(object):
request.virtual_root = vroot
request.virtual_root_path = vroot_path
- security_policy = self.security_policy
-
- permission = None
-
- if security_policy is not None:
- permission = registry.queryMultiAdapter((context, request),
- IViewPermission,
- name=view_name)
+ if self.secured:
- debug_authorization = self.debug_authorization
+ permitted = registry.queryMultiAdapter((context, request),
+ IViewPermission,
+ name=view_name)
- permitted = _view_execution_permitted(context, request, view_name,
- security_policy, permission,
- debug_authorization)
+ if permitted is None:
+ if self.debug_authorization:
+ permitted = Allowed(
+ 'Allowed: view name %r in context %r (no '
+ 'permission registered).' %
+ (view_name, context))
+ else:
+ permitted = True
- logger = self.logger
+
+ else:
+ if self.debug_authorization:
+ permitted = Allowed(
+ 'Allowed: view name %r in context %r (no '
+ 'authentication policy in use).' % (view_name, context))
+ else:
+ permitted = True
- if debug_authorization:
+ if self.debug_authorization:
logger and logger.debug(
'debug_authorization of url %s (view name %r against '
'context %r): %s' % (
request.url, view_name, context, permitted)
)
+
if not permitted:
- if debug_authorization:
+ if self.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
+
+ response = self.forbidden_resp_factory(context, request)
+ start_response(response.status, response.headerlist)
+ return response.app_iter
response = registry.queryMultiAdapter(
(context, request), IView, name=view_name)
@@ -168,7 +223,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)
@@ -182,28 +237,89 @@ class Router(object):
'Non-response object returned from view: %r' % response)
finally:
- registry_manager.pop()
+ self.threadlocal_manager.pop()
+
+def default_forbidden_view(context, request):
+ status = '401 Unauthorized'
+ try:
+ msg = escape(request.environ['repoze.bfg.message'])
+ except KeyError:
+ msg = ''
+ html = """
+ <html>
+ <title>%s</title>
+ <body>
+ <h1>%s</h1>
+ <code>%s</code>
+ </body>
+ </html>
+ """ % (status, status, msg)
+ headers = [('Content-Length', str(len(html))),
+ ('Content-Type', 'text/html')]
+ response_factory = queryUtility(IResponseFactory, default=Response)
+ return response_factory(status = status,
+ headerlist = headers,
+ app_iter = [html])
def make_app(root_factory, package=None, filename='configure.zcml',
- options=None):
- """ Return a Router object, representing a ``repoze.bfg`` WSGI
- application. ``root_factory`` must be a callable that accepts a
- WSGI environment and returns a root object. ``package`` is a
- Python module representing the application's package, ``filename``
- is the filesystem path to a ZCML file (optionally relative to the
- package path) that should be parsed to create the application
- registry. ``options``, if used, should be a dictionary containing
- runtime options (e.g. the key/value pairs in an app section of a
+ authentication_policy=None, authorization_policy=None,
+ options=None, registry=None, debug_logger=None):
+ # registry and debug_logger *only* for unittests
+ """ Return a Router object, representing a fully configured
+ ``repoze.bfg`` WSGI application.
+
+ ``root_factory`` must be a callable that accepts a WSGI
+ environment and returns a traversal root object. It may be
+ ``None``, in which case traversal is not performed at all.
+ Instead, all URL-to-code mapping is done via URL dispatch (aka
+ Routes).
+
+ ``package`` is a Python module representing the application's
+ package. It is optional, defaulting to ``None``. If ``package``
+ is ``None``, the ``filename`` passed must be an absolute pathname
+ to a ZCML file that represents the application's configuration.
+
+ ``filename`` is the filesystem path to a ZCML file (optionally
+ relative to the package path) that should be parsed to create the
+ application registry. It defaults to ``configure.zcml``.
+
+ ``authentication_policy`` should be an object that implements the
+ ``repoze.bfg.interfaces.IAuthenticationPolicy`` interface (e.g.
+ it might be an instance of
+ ``repoze.bfg.authentication.RemoteUserAuthenticationPolicy``) or
+ ``None``. If ``authentication_policy`` is ``None``, no
+ authentication or authorization will be performed. Instead, BFG
+ will ignore any view permission assertions in your application and
+ imperative security checks performed by your application will
+ always return ``True``.
+
+ ``authorization_policy`` is an object that implements the
+ ``repoze.bfg.interfaces.IAuthorizationPoicy`` interface
+ (notionally) or ``None``. If the ``authentication_policy``
+ argument is ``None``, this argument is ignored entirely because
+ being able to authorize access to a user depends on being able to
+ authenticate that user. If the ``authentication_policy`` argument
+ is *not* ``None``, and the ``authorization_policy`` argument *is*
+ ``None``, the authorization policy defaults to an authorization
+ implementation that uses ACLs.
+
+ ``options``, if used, should be a dictionary containing runtime
+ options (e.g. the key/value pairs in an app section of a
PasteDeploy file), with each key representing the option and the
key's value representing the specific option value,
e.g. ``{'reload_templates':True}``"""
if options is None:
options = {}
+
regname = filename
+
if package:
regname = package.__name__
- registry = Registry(regname)
- debug_logger = make_stream_logger('repoze.bfg.debug', sys.stderr)
+ if registry is None:
+ registry = Registry(regname)
+
+ if debug_logger is None:
+ debug_logger = make_stream_logger('repoze.bfg.debug', sys.stderr)
registry.registerUtility(debug_logger, ILogger, 'repoze.bfg.debug')
settings = Settings(options)
registry.registerUtility(settings, ISettings)
@@ -221,18 +337,39 @@ def make_app(root_factory, package=None, filename='configure.zcml',
'root_factory (aka get_root) was None and no routes connected')
registry.registerUtility(root_factory, IRootFactory)
+
+ if authentication_policy:
+ registry.registerUtility(authentication_policy, IAuthenticationPolicy)
+ if authorization_policy is None:
+ authorization_policy = ACLAuthorizationPolicy()
+ registry.registerUtility(authorization_policy, IAuthorizationPolicy)
+ else:
+ # deal with bw compat of <= 0.8 security policies (deprecated)
+ secpol = registry.queryUtility(ISecurityPolicy)
+ if secpol is not None:
+ debug_logger.warn(
+ 'Your application is using a repoze.bfg ``ISecurityPolicy`` '
+ '(probably registered via ZCML). This form of security policy '
+ 'has been deprecated in BFG 0.9. See the "Security" chapter '
+ 'of the repoze.bfg documentation to see how to register a more '
+ 'up to date set of security policies (an authentication '
+ 'policy and an authorization policy). ISecurityPolicy-based '
+ 'security policies will cease to work in a later BFG '
+ 'release.')
+ registerBBBAuthn(secpol, registry)
+
app = Router(registry)
# We push the registry on to the stack here in case any ZCA API is
# used in listeners subscribed to the WSGIApplicationCreatedEvent
# we send.
- registry_manager.push(registry)
+ manager.push({'registry':registry, 'request':None})
try:
# use dispatch here instead of registry.notify to make unit
# tests possible
dispatch(WSGIApplicationCreatedEvent(app))
finally:
- registry_manager.pop()
+ manager.pop()
return app