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)