Author: rjollos
Date: Thu Jun 20 23:36:12 2013
New Revision: 1495238
URL: http://svn.apache.org/r1495238
Log:
Refs #514: Implemented authentication middleware for custom web bootstrap
handlers. Patch by Olemis.
Modified:
bloodhound/trunk/bloodhound_multiproduct/multiproduct/hooks.py
bloodhound/trunk/trac/trac/hooks.py
bloodhound/trunk/trac/trac/web/standalone.py
Modified: bloodhound/trunk/bloodhound_multiproduct/multiproduct/hooks.py
URL:
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_multiproduct/multiproduct/hooks.py?rev=1495238&r1=1495237&r2=1495238&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_multiproduct/multiproduct/hooks.py (original)
+++ bloodhound/trunk/bloodhound_multiproduct/multiproduct/hooks.py Thu Jun 20
23:36:12 2013
@@ -120,4 +120,5 @@ class ProductRequestWithSession(RequestW
class ProductRequestFactory(RequestFactoryBase):
def create_request(self, env, environ, start_response):
- return ProductRequestWithSession(env, environ, start_response)
+ return ProductRequestWithSession(env, environ, start_response) \
+ if env else RequestWithSession(environ, start_response)
Modified: bloodhound/trunk/trac/trac/hooks.py
URL:
http://svn.apache.org/viewvc/bloodhound/trunk/trac/trac/hooks.py?rev=1495238&r1=1495237&r2=1495238&view=diff
==============================================================================
--- bloodhound/trunk/trac/trac/hooks.py (original)
+++ bloodhound/trunk/trac/trac/hooks.py Thu Jun 20 23:36:12 2013
@@ -107,6 +107,12 @@ class BootstrapHandlerBase(object):
|| trac.locale || || Target locale ||
|| trac.base_url || TRAC_BASE_URL || Trac base URL hint ||
+ A new entry named 'trac.env_name' identifying environment SHOULD be
+ added (e.g. used by tracd to choose authentication realms).
+ As a side-effect the WSGI environment dict (i.e. `environ`) may be
+ modified in many different ways to prepare it for subsequent
+ dispatching.
+
This method may handle the request (e.g. render environment index page)
in case environment lookup yields void results. In that case it MUST
invoke WSGI `write` callable returned by `start_response` and raise
@@ -114,6 +120,7 @@ class BootstrapHandlerBase(object):
:param environ: WSGI environment dict
:param start_response: WSGI callback for starting the response
+ :return: environment object
:throws RequestDone: if the request is fully processed while loading
target environment e.g. environment index page
:throws EnvironmentError: if it is impossible to find a way to locate
@@ -125,6 +132,45 @@ class BootstrapHandlerBase(object):
"""
raise NotImplementedError("Must override method 'open_environment'")
+ def default_probe_environment(self, environ):
+ """By default it will invoke `open_environment` and discard the
+ resulting environment object. This approach is generic but not
+ efficient. Should be overridden whenever possible.
+ """
+ # If the expected configuration keys aren't found in the WSGI
environment,
+ # try looking them up in the process environment variables
+ environ.setdefault('trac.env_path', os.getenv('TRAC_ENV'))
+ environ.setdefault('trac.env_parent_dir',
+ os.getenv('TRAC_ENV_PARENT_DIR'))
+ environ.setdefault('trac.env_index_template',
+ os.getenv('TRAC_ENV_INDEX_TEMPLATE'))
+ environ.setdefault('trac.template_vars',
+ os.getenv('TRAC_TEMPLATE_VARS'))
+ environ.setdefault('trac.locale', '')
+ environ.setdefault('trac.base_url',
+ os.getenv('TRAC_BASE_URL'))
+
+ try:
+ self.open_environment(environ,
+ lambda status, headers: (lambda data: None))
+ except Exception:
+ # Handle all exceptions; else potential HTTP protocol violation
+ pass
+
+ def probe_environment(self, environ):
+ """This method is aimed at providing a lightweight version of
+ `open_environment` by solely applying upon `environ` the side effects
+ needed to dispatch the request in environment context.
+
+ By default it will invoke `open_environment` and discard the
+ resulting environment object. Specialized versions will have the chance
+ to implement more efficient strategies in case environment
+ instantiation may be avoided.
+
+ :return: None
+ """
+ self.default_probe_environment(environ)
+
def create_request(self, env, environ, start_response):
"""Instantiate request object used in subsequent request dispatching
@@ -148,7 +194,9 @@ class DefaultBootstrapHandler(BootstrapH
def open_environment(self, environ, start_response):
env_path = environ.get('trac.env_path')
- if not env_path:
+ if env_path:
+ environ['trac.env_name'] = os.path.basename(env_path)
+ else:
env_parent_dir = environ.get('trac.env_parent_dir')
env_paths = environ.get('trac.env_paths')
if env_parent_dir or env_paths:
@@ -163,7 +211,8 @@ class DefaultBootstrapHandler(BootstrapH
send_project_index(environ, start_response, env_parent_dir,
env_paths)
raise RequestDone
-
+
+ environ['trac.env_name'] = env_name
errmsg = None
# To make the matching patterns of request handlers work, we
append
Modified: bloodhound/trunk/trac/trac/web/standalone.py
URL:
http://svn.apache.org/viewvc/bloodhound/trunk/trac/trac/web/standalone.py?rev=1495238&r1=1495237&r2=1495238&view=diff
==============================================================================
--- bloodhound/trunk/trac/trac/web/standalone.py (original)
+++ bloodhound/trunk/trac/trac/web/standalone.py Thu Jun 20 23:36:12 2013
@@ -27,6 +27,7 @@ import sys
from SocketServer import ThreadingMixIn
from trac import __version__ as VERSION
+from trac.hooks import load_bootstrap_handler
from trac.util import autoreload, daemon
from trac.web.auth import BasicAuthentication, DigestAuthentication
from trac.web.main import dispatch_request
@@ -59,6 +60,31 @@ class AuthenticationMiddleware(object):
return self.application(environ, start_response)
+class BootstrapAuthenticationMiddleware(AuthenticationMiddleware):
+ """Authentication middleware for custom web bootstrap handlers
+ """
+ def __call__(self, environ, start_response):
+ bootstrap_ep = os.getenv('TRAC_BOOTSTRAP_HANDLER')
+ environ.setdefault('trac.bootstrap_handler', bootstrap_ep)
+
+ # Preserve original environ and probe dispatching
+ temp_environ = environ.copy()
+ bootstrap = load_bootstrap_handler(bootstrap_ep)
+ bootstrap.probe_environment(temp_environ)
+
+ path_info = temp_environ.get('PATH_INFO', '')
+ path_parts = filter(None, path_info.split('/'))
+ env_name = temp_environ.get('trac.env_name')
+ if path_parts and path_parts[0] == 'login' and env_name:
+ auth = self.auths.get(env_name, self.auths.get('*'))
+ if auth:
+ remote_user = auth.do_auth(environ, start_response)
+ if not remote_user:
+ return []
+ environ['REMOTE_USER'] = remote_user
+ return self.application(environ, start_response)
+
+
class BasePathMiddleware(object):
def __init__(self, application, base_path):
@@ -269,15 +295,19 @@ def main():
if parser.has_option('pidfile') and options.pidfile:
options.pidfile = os.path.abspath(options.pidfile)
- wsgi_app = TracEnvironMiddleware(dispatch_request,
- options.env_parent_dir, args,
- options.single_env)
+ wsgi_app = dispatch_request
+
if auths:
if options.single_env:
project_name = os.path.basename(args[0])
- wsgi_app = AuthenticationMiddleware(wsgi_app, auths, project_name)
+ wsgi_app = BootstrapAuthenticationMiddleware(wsgi_app, auths,
project_name)
else:
- wsgi_app = AuthenticationMiddleware(wsgi_app, auths)
+ wsgi_app = BootstrapAuthenticationMiddleware(wsgi_app, auths)
+
+ wsgi_app = TracEnvironMiddleware(wsgi_app,
+ options.env_parent_dir, args,
+ options.single_env)
+
base_path = options.base_path.strip('/')
if base_path:
wsgi_app = BasePathMiddleware(wsgi_app, base_path)