From 86ed4016ea6a681d4f579ace62cea032a679544d Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sun, 24 May 2009 23:12:59 +0000 Subject: 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``. --- repoze/bfg/router.py | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) (limited to 'repoze/bfg/router.py') 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) -- cgit v1.2.3